“您的个人假期”旅行社组织了一次比荷卢经济联盟的巴士之旅。
比荷卢经济联盟有很多公交线路。
每天公共汽车都会从一座城市开往另一座城市。
沿途汽车可能会在一些城市(零或更多)停靠。
旅行社计划旅途从 S 城市出发,到 F 城市结束。
由于不同旅客的景点偏好不同,所以为了迎合更多旅客,旅行社将为客户提供多种不同线路。
游客可以选择的行进路线有所限制,要么满足所选路线总路程为 S 到 F 的最小路程,要么满足所选路线总路程仅比最小路程多一个单位长度。
如上图所示,如果 S=1,F=5,则这里有两条最短路线 1→2→5,1→3→5,长度为 6;有一条比最短路程多一个单位长度的路线 1→3→4→5,长度为 7。
现在给定比荷卢经济联盟的公交路线图以及两个城市 S 和 F,请你求出旅行社最多可以为旅客提供多少种不同的满足限制条件的线路。
输入格式
第一行包含整数 TT,表示共有 TT 组测试数据。
每组数据第一行包含两个整数 N 和 M,分别表示总城市数量和道路数量。
接下来 MM 行,每行包含三个整数 A,B,L,表示有一条线路从城市 A 通往城市 B,长度为 LL。
需注意,线路是 单向的,存在从 A 到 B 的线路不代表一定存在从 B 到 A 的线路,另外从城市 AA 到城市 BB 可能存在多个不同的线路。
接下来一行,包含两个整数 S 和 F,数据保证 S 和 F 不同,并且 S、F 之间至少存在一条线路。
输出格式
每组数据输出一个结果,每个结果占一行。
数据保证结果不超过 109。
数据范围
2≤N≤1000,
1≤M≤10000,
1≤L≤1000,
1≤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
/*
解题思路:
与最短路计数:https://www.acwing.com/problem/content/1136/类似,只不过
该题要记录两个点而最短路计数只是记录一个点
*/
#include <queue> #include <vector> #include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N = 1010, M = 10010; struct Ver//重载大于号表示小根堆 { int ver, type, dist; bool operator > (const Ver &W) const { return dist > W.dist; } }; int n, m;//点数与边数 int S, E;//起点与终点 int cnt[N][2];//cnt[N][0]表示最短路的方案数, cnt[N][1]表示次短路的方案数 bool st[N][2];//st[N][0]表示当前最短路是否走过,st[N][1]表示当前次短路是否走过 int dist[N][2];//dist[N][0]表示最短路走过的距离,dist[N][1]表示次短路走过的距离 int h[N], e[M], ne[M], w[M], idx;//邻接表 void add(int a, int b, int c) { e[idx] = b; w[idx] = c; ne[idx] = h[a]; h[a] = idx; idx ++ ; } int dijkstra() { memset(st, false, sizeof st); memset(dist, 0x3f, sizeof dist); priority_queue<Ver, vector<Ver>, greater<Ver>> heap;//小根堆 cnt[S][0] = 1;//从起点S开始的最短路数目为1条,无次短路 dist[S][0] = 0;//下面同理如上 heap.push({S, 0, dist[S][0]});//第一维为当前枚举到哪个点,第二维当前枚举的类型0表示最短路 //1表示次短路,第三维表示距离 while (heap.size()) { auto t = heap.top(); heap.pop(); int ver = t.ver, type = t.type, distance = t.dist, count = cnt[ver][type]; //count表示走到当前点ver类型为type的方案数 if (st[ver][type]) continue;//dijkstra的方案数 st[ver][type] = true; for (int i = h[ver]; i != -1; i = ne[i]) { int j = e[i]; if (dist[j][0] > dist[ver][type] + w[i])//若最短路可以被更新 { cnt[j][1] = cnt[j][0];//先将次短路更新为原最短路 dist[j][1] = dist[j][0]; heap.push({j, 1, dist[j][1]}); cnt[j][0] = cnt[ver][type];//再将最短路更新 dist[j][0] = dist[ver][type] + w[i]; heap.push({j, 0, dist[j][0]}); } else if (dist[j][0] == dist[ver][type] + w[i]) cnt[j][0] += count; else if (dist[j][1] > dist[ver][type] + w[i])//若次短路可以被更新 { cnt[j][1] = cnt[ver][type];//更新次短路 dist[j][1] = dist[ver][type] + w[i]; heap.push({j, 1, dist[j][1]}); } else if (dist[j][1] == dist[ver][type] + w[i]) cnt[j][1] += count; } } int res = cnt[E][0]; if (dist[E][1] == dist[E][0] + 1) res += cnt[E][1];//若满足题意: //满足所选路线总路程仅比最小路程多一个单位长度 //则加上次短路的方案数 return res; } int main() { int T; cin >> T; while (T -- ) { idx = 0;//多组测试数据每轮都要初始化 memset(h, -1, sizeof h); scanf("%d %d", &n, &m); while (m -- ) { int a, b, c; scanf("%d %d %d", &a, &b, &c); add(a, b, c);//有向图只记录一条边 } cin >> S >> E; cout << dijkstra() << endl; } return 0; }