Problem C: 最短路径

最近又在牛客网刷到这个题目了,题目链接

然而,很悲催地,还是按着常规的思路采用最短路径写,依旧WA。一方面,之前刷过的题目,还是得经常温故,另一方面,训练的题目还是太少,继续加油~

Description

N个城市,标号从0到N-1,M条道路,第K条道路(K从0开始)的长度为2^K,求编号为0的城市到其他城市的最短距离。

Input

第一行两个正整数N(2<=N<=100)M(M<=500),表示有N个城市,M条道路,
接下来M行两个整数,表示相连的两个城市的编号。

Output

N-1行,表示0号城市到其他城市的最短路,如果无法到达,输出-1,数值太大的以MOD 100000 的结果输出。

Sample Input

4 3
0 1
1 2
2 0

Sample Output

1
3
-1

参考链接:1956 Problem C 最短路径

最终AC代码如下:

#include <bits/stdc++.h>
using namespace std;
const int MAX=105, MOD=1e5, INF=-1; //这里注意INF是-1而不是无穷大(虽然本题写成无穷大也能通过)
int father[MAX], d[MAX][MAX];
int findFather(int x){ //找父节点 
    if(x != father[x]) father[x] = findFather(father[x]);  //递归寻找父节点并压缩路径 
    return father[x];
}
int quickMi(int a, int k){
    int ans = 1;
    long long int res = a; //这里res用int容易溢出
    while(k){
        if(k & 1) ans = (ans * res) % MOD;
        res = (res * res) % MOD; //这里不要写成 res = (res * a) % MOD; 
        k >>= 1;
    }
    return ans;
}
int main(){
    int i, j, k, u, v, fu, fv, n, m, dis;
    while(scanf("%d %d", &n, &m) != EOF){
        fill(d[0], d[0]+MAX*MAX, INF); //初始化距离数组
        for(i=0; i<n; i++){
            father[i] = i; //初始化并查集
            d[i][i] = 0; //初始化距离数组对角线 这里忘记 答案一定错 
        }
        for(k=0; k<m; k++){
            scanf("%d %d", &u, &v);
            fu = findFather(u); //找u的父节点fu 
            fv = findFather(v);
            if(fu != fv){ //说明u,v未连通 
                dis = quickMi(2, k); //由于指数太大 采用快速幂 
                for(i=0; i<n; i++){
                    if(fu == findFather(i)){ //找到以 x 为根节点的集合上所有点 
                        for(j=0; j<n; j++){
                            if(fv == findFather(j)){ //找到以 y 为根节点的集合上所有点 
                                //注意以下更新d[i][j]和d[j][i]一起更新 
                                d[i][j] = d[j][i] = (d[i][u] + dis + d[v][j]) % MOD; //更新 
                            }
                        }
                    }
                }
                father[fu] = fv; //合并这两个集合
            }
        }
        for(j=1; j<n; j++) printf("%d
", d[0][j]);
    }
    return 0;
}

总结:这题的关键麻烦就是指数可能会很大,于是不能用一半的求最短路的方式求解。然后,由求快速幂取模的方式,可以解决指数幂次很大的情况。于是存储取余后的路径后,再尝试用Dijkstra等求最短路径,结果依然是WA,原因是路径取模不精确。

后来,参考别人的博客时,发现别人采用快速幂+并查集的方式很简便的解决了,主要思想是:如果两个点已经在一个集合中了,由于后面输入的边是递增的,所以不用再更新;如果两个点在两个不同集合上,那么说明这两个集合没有边连接,那么更新路径数组,再合并集合。

原文地址:https://www.cnblogs.com/heyour/p/12636148.html