[模板] 单源最短路径 - 最短路计数 (洛谷P1144 最短路计数)

[模板] 单源最短路径 - 最短路计数 (洛谷P1144 最短路计数)

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的过程:每次都从当前点去寻找更短的路径,如果找到更短的路,那么松弛该点并且更新到当前点的最短路。

对于最短路计数问题:我们考虑在尝试松弛该点时的三种情况:

  1. 从该边更新时,到该边终点的最短路大于已经找到的最短路(无法从该边做到更短),那么忽略这条边;
  2. 从该边更新时,到该边终点的最短路能够被更新(找到更短的路),那么该边终点的最短路计数即为该边起点的最短路计数;
  3. 从该边更新时,到该边终点的最短路与之前找到的最短路长度相同,那么该边终点的最短路计数即为该边现有的最短路计数+从该边起点的最短路计数。

代码

// 最短路计数
#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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值