zoukankan      html  css  js  c++  java
  • 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,原因是路径取模不精确。

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

  • 相关阅读:
    Football Foundation (FOFO) TOJ 2556
    JAVA- String类练习
    JAVA- 清除数组重复元素
    Mysql远程登陆错误:ERROR 2003
    Linux学习之路(五)压缩命令
    Linux学习之路(四)帮助命令
    如何识别真Microsoft服务与非Microsoft服务来定位病毒自己的服务
    如何用命令行删除EasyBCD开机选择项?
    JAVA- 成员变量与局部变量的区别
    JAVA- 内部类
  • 原文地址:https://www.cnblogs.com/heyour/p/12636148.html
Copyright © 2011-2022 走看看