AcWing 383. 观光 ( dijkstra拆点求最短路数)

本文介绍了一种解决比荷卢经济联盟巴士之旅路线规划问题的算法,旨在为旅行社提供从起点到终点的最短路径及次短路径的线路数量,满足游客的不同需求。采用Dijkstra算法结合堆优化,对每个节点进行拆点处理,区分最短路径与次短路径,确保结果准确性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

整理的算法模板:ACM算法模板总结(分类详细版)

 

“您的个人假期”旅行社组织了一次比荷卢经济联盟的巴士之旅。

比荷卢经济联盟有很多公交线路。

每天公共汽车都会从一座城市开往另一座城市。

沿途汽车可能会在一些城市(零或更多)停靠。

旅行社计划旅途从 S 城市出发,到 F 城市结束。

由于不同旅客的景点偏好不同,所以为了迎合更多旅客,旅行社将为客户提供多种不同线路。

游客可以选择的行进路线有所限制,要么满足所选路线总路程为 S 到 F 的最小路程,要么满足所选路线总路程仅比最小路程多一个单位长度。

3463_1.png

如上图所示,如果S = 1,F = 5,则这里有两条最短路线1->2->5,1->3->5,长度为6;有一条比最短路程多一个单位长度的路线1->3->4->5,长度为7。

现在给定比荷卢经济联盟的公交路线图以及两个城市 S 和 F,请你求出旅行社最多可以为旅客提供多少种不同的满足限制条件的线路。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含两个整数 N 和 M,分别表示总城市数量和道路数量。

接下来 M 行,每行包含三个整数 A,B,L,表示有一条线路从城市 A 通往城市 B,长度为 L。

需注意,线路是 单向的,存在从A到B的线路不代表一定存在从B到A的线路,另外从城市A到城市B可能存在多个不同的线路。

接下来一行,包含两个整数 S 和 F,数据保证 S 和 F 不同,并且S、F之间至少存在一条线路。

输出格式

每组数据输出一个结果,每个结果占一行。

数据保证结果不超过109109。

数据范围

2≤N≤10002≤N≤1000,
1≤M≤100001≤M≤10000,
1≤L≤10001≤L≤1000,
1≤A,B,S,F≤N1≤A,B,S,F≤N

输入样例:

2
5 8
1 2 3
1 3 2
1 4 5
2 3 1
2 5 3
3 4 2
3 5 4
4 5 3
1 5
5 6
2 3 1
3 2 1
3 1 10
4 5 2
5 2 7
5 2 7
4 1

输出样例:

3
2
难度:中等
时/空限制:1s / 64MB
总通过数:374
总尝试数:843
来源:《算法竞赛进阶指南》
算法标签 :最短路

首先最短路计数问题最好用dijkstra算法(权值0,1的图可以直接bfs进行宽搜);因为dijkstra具有天生的拓扑序;假设算当前点u的时候要用到前面用过的点v,那么不可能下次用u来更新v;也就说没有0环的存在;spfa算法不具有拓扑序,但可以转换为最短路树问题计算;

这道题可以对每个点进行拆点;以前一个点有两个信息:最短距离  点的id;拆点之后多了一个信息:type;这个type代表的这个点的类型;那么这两个点分别是  最短距离点  和  次短距离点;然后按照dijkstra+heap跑一遍就可;

注意,一定要先更新次短路点的信息,因为最短路没有更新的时候,次最短路可能更新;而次最短路更新的时候,最短路一定会更新;

#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<pair<int,int>,int> PII;

int n, m, S, F;
const int N=1010,M=20010;
int h[N], e[M], w[M], ne[M], idx;
int dis[N][2], cnt[N][2];
bool st[N][2];
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int dijkstra(int root)
{
    memset(dis,0x3f,sizeof dis);
    memset(st,false,sizeof st);
    memset(cnt,0,sizeof cnt);
    dis[root][0]=0,cnt[root][0]=1;
    priority_queue<PII,vector<PII>,greater<PII> > heap;
    heap.push({{0,0},root});
    while(!heap.empty())
    {
        auto t=heap.top();
        heap.pop();
        int ver=t.y,type=t.x.y,distance=t.x.x,res=cnt[ver][type];
        if(st[ver][type]) continue;
        st[ver][type] = true;
        for(int i=h[ver];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dis[j][0]>distance+w[i])
            {
                dis[j][1]=dis[j][0],cnt[j][1]=cnt[j][0];
                heap.push({{dis[j][1],1},j});
                dis[j][0]=distance+w[i],cnt[j][0]=res;
                heap.push({{dis[j][0],0},j});
            }
            else if(dis[j][0]==distance + w[i]) cnt[j][0]+=res;
            else if(dis[j][1]>distance+w[i])
            {
                dis[j][1]=distance+w[i];
                cnt[j][1]=res;
                heap.push({{dis[j][1],1},j});
            }
            else if(dis[j][1]==distance + w[i])
            {
                cnt[j][1]+=res;
            }
            
        }
    }
    int ans = cnt[F][0];
    if (dis[F][0] + 1 == dis[F][1]) ans += cnt[F][1];

    return ans;
}
int main()
{
    int t;
    cin >>t;
    while(t--)
    {
        idx=0;
        memset(h,-1,sizeof h);
        int n,m;
        cin >>n>>m;
        for(int i=0;i<m;i++)
        {
            int a,b,c;
            cin >>a>>b>>c;
            add(a,b,c);
        }
        cin >>S>>F;
        
        cout <<dijkstra(S)<<endl;
    }
    
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值