洛谷题单指南-最短路-P1144 最短路计数
原题链接:https://www.luogu.com.cn/problem/P1144
题意解读:计算所有节点的最短路条数。
解题思路:
在使用BFS、Dijikstra计算最短路的过程中,一个节点如果能更新最短路,必然是被一个已经更新过最短路的节点,也就是说,
在更新最短路的时候,可以同时更新其最短路的条数,设dist[N]表示节点的最短路,cnt[N]表示节点最短路的条数
如果对于一个已经确定最短路的节点u,有一条邻边u->v,权值w,
情况1、如果v的最短路可以更新,条件满足dist[v] > dist[u] + w时,
则有dist[v] = dist[u] + w,同时最短路的条数也可以更新:cnt[v] = cnt[u]
由于最短路条数不止一条,后续的节点同样有可能得到与v相同的最短路
情况2、如果v的最短路有重复,条件满足dist[v] == dist[u] + w时,
则最短路的值不需要更新,但是最短路的条数需要更新:cnt[v] += cnt[u]
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005, M = 4000005, MOD = 100003;
int h[N], e[M], ne[M], idx;
int dist[N], cnt[N];
bool vis[N];
int n, m;
void add(int a, int b)
{
e[++idx] = b;
ne[idx] = h[a];
h[a] = idx;
}
void bfs()
{
memset(dist, 0x3f, sizeof(dist));
dist[1] = 0;
cnt[1] = 1;
queue<int> q;
q.push(1);
while(q.size())
{
int u = q.front(); q.pop();
vis[u] = true;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(dist[v] > dist[u] + 1)
{
dist[v] = dist[u] + 1;
cnt[v] = cnt[u];
if(!vis[v]) q.push(v);
}
else if(dist[v] == dist[u] + 1)
{
cnt[v] = (cnt[v] + cnt[u]) % MOD;
}
}
}
}
int main()
{
cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
memset(h, -1, sizeof(h));
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
bfs();
for(int i = 1; i <= n; i++) cout << cnt[i] << endl;
return 0;
}
拓展思考:
对于本题,由于是无权图,用BFS、Dijikstra都能实现,因为一个节点更新领接点时,其已经确定过最短路。
但是SPFA算法则不具备这样的特性,也就是不具备拓扑序。
如果是负权图,则必须使用SPFA算法,方法如下:
1、先用SPFA计算所有节点的最短路
2、再枚举所有边,如果满足dist[v] = dist[u] + w,表示u是v计算最短路的前驱,可以在新的拓扑图建立u->v的一条边,更新v的入度
3、对新的拓扑图进行拓扑排序,在拓扑排序的过程中,累加每个节点的最短路数量即可
浙公网安备 33010602011771号