洛谷题单指南-最短路-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、对新的拓扑图进行拓扑排序,在拓扑排序的过程中,累加每个节点的最短路数量即可

 

posted @ 2025-04-09 11:36  hackerchef  阅读(52)  评论(0)    收藏  举报