[模板] 单源最短路径 - 最短路计数 (洛谷P1144 最短路计数)
题目描述
给出一个N个顶点M条边的无向无权图,顶点编号为1-N。问从顶点1开始,到其他每个点的最短路有几条。
输入格式
第一行包含2个正整数N,M,为图的顶点数与边数。
接下来M行,每行22个正整数x,y,表示有一条顶点x连向顶点y的边,请注意可能有自环与重边。
输出格式
共N行,每行一个非负整数,第ii行输出从顶点1到顶点i有多少条不同的最短路,由于答案有可能会很大,你只需要输出ans %100003后的结果即可。如果无法到达顶点i则输出0。
输入输出样例
输入 #1 | 输出 #1 |
---|---|
5 7 1 2 1 3 2 4 3 4 2 3 4 5 4 5 | 1 1 1 2 4 |
说明/提示
1到5的最短路有4条,分别为2条1−2−4−5和2条1−3−4−5(由于4−5的边有2条)。
对于20%的数据,N≤100;
对于60%的数据,N≤1000;
对于100%的数据,N<=1000000,M<=2000000。
思路
考虑spfa的过程:每次都从当前点去寻找更短的路径,如果找到更短的路,那么松弛该点并且更新到当前点的最短路。
对于最短路计数问题:我们考虑在尝试松弛该点时的三种情况:
- 从该边更新时,到该边终点的最短路大于已经找到的最短路(无法从该边做到更短),那么忽略这条边;
- 从该边更新时,到该边终点的最短路能够被更新(找到更短的路),那么该边终点的最短路计数即为该边起点的最短路计数;
- 从该边更新时,到该边终点的最短路与之前找到的最短路长度相同,那么该边终点的最短路计数即为该边现有的最短路计数+从该边起点的最短路计数。
代码
// 最短路计数
#include <cstdio>
#include <cstring>
#include <queue>
#define get32(__A) scanf("%d", &__A)
#define put32(__A) printf("%d ", __A)
#define NODE_NUM 1000000 + 5
#define EDGE_NUM 2000000 + 5
#define MOD_VAL 100003
// 链式前向星
struct EdgeStr{
int from, to, len;
} edge[EDGE_NUM * 2];
int first[NODE_NUM];
int next[EDGE_NUM * 2];
void add_edge(const int &from, const int &to, const int &len){
static int k = 0;
++k;
edge[k] = {from, to, len};
next[k] = first[from];
first[from] = k;
}
// spfa
int n, m;
int distance[NODE_NUM];
bool visit[NODE_NUM];
// 最短路计数
int count[NODE_NUM];
void spfa(const int &source){
using std::queue;
// 初始化
memset(distance, 0x3f, sizeof(distance));
queue<int> q;
distance[source] = 0;
visit[source] = true;
count[source] = 1;
q.push(source);
while (!q.empty()){
int thisPoint = q.front();
q.pop();
visit[thisPoint] = false;
for (int i = first[thisPoint]; i; i = next[i]){
const EdgeStr &thisEdge = edge[i];
// 找到相同长度的路,增加计数
if (distance[thisEdge.to] == distance[thisEdge.from] + thisEdge.len){
count[thisEdge.to] += count[thisEdge.from];
count[thisEdge.to] %= MOD_VAL;
}
// 找到更短的路,覆盖以前的计数
else if (distance[thisEdge.to] > distance[thisEdge.from] + thisEdge.len){
distance[thisEdge.to] = distance[thisEdge.from] + 1;
count[thisEdge.to] = count[thisEdge.from];
count[thisEdge.to] %= MOD_VAL;
if (!visit[thisEdge.to]){
q.push(thisEdge.to);
visit[thisEdge.to] = true;
}
}
}
}
}
int main(){
get32(n);
get32(m);
for (int i = 1, point1, point2; i <= m; i++){
get32(point1);
get32(point2);
add_edge(point1, point2, 1);
add_edge(point2, point1, 1);
}
spfa(1);
for (int i = 1; i <= n; i++){
put32(count[i]);
putchar('\n');
}
return 0;
}