九度 OJ1100 最短路径(需要使用高精度整数)

探讨了在特定条件下的最短路径问题,提出了一种创新的解决方案,即将其转化为最小生成树问题,并通过并查集算法高效求解。同时,对比了Floyd算法的大整数运算处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(九度教程第题)

时间限制:1 秒 内存限制:32 兆 特殊判题:否

1.题目描述:

N个城市,标号从0到N-1,M条道路,第K条道路(K从0开始)的长度为2^K,求编号为0的城市到其他城市的最短距离
输入描述:
第一行两个正整数N(2<=N<=100)M(M<=500),表示有N个城市,M条道路
接下来M行两个整数,表示相连的两个城市的编号
输出描述:
N-1行,表示0号城市到其他城市的最短路,如果无法到达,输出-1,数值太大的以MOD 100000 的结果输出。
示例1
输入
4 4
1 2
2 3
1 3
0 1
输出
8
9
11

2.基本思路

该题涉及到大精度整数的运算。如果要采用Floyd算法来求解问题,那么需要解决大精度整数的问题,那么可以采用java的BigInteger类来实现。
但这里重点讲解另一种清奇的思路,就是这个问题其实是披着最短路外皮的最小生成树问题。可以注意到第i条边的长度为 2 i , i ∈ ( 0 , 1 , . . . , m ) 2^i,i∈(0,1,...,m) 2i,i(0,1,...,m),因此对于新输入的两条边a,b,此时讨论两种情况:

  • ①如果a和b属于同一个集合那么当前边就直接舍弃掉,因为之前的所有边之和都不会大于该边 ( 2 0 + 2 1 + , . . . , + 2 m − 1 &lt; 2 m ) (2^0+2^1+,...,+2^{m-1}&lt;2^m) (20+21+,...,+2m1<2m),而且此时图又是连通的,故舍弃该边。
  • ②如果a和b不属于同一个集合,那么找到a的父节点x,b的父节点y。遍历x和y下的所有子节点,通过a-b的边将各自的子节点连接起来,随后将两个集合合并。

3.代码实现

并查集

#include <iostream>
#define N 101

using namespace std;

int dis[N][N];
int Tree[N];

int mod(int x,int y){//求2^n,并对100000求模
    int ans=1;
    for(int i=1;i<=y;i++){
        ans = (ans*x)%100000;
    }

    return ans;

}

int findRoot(int x){//获得某个结点父节点的编号

    if(Tree[x]==-1)
        return x;
    else{
        int tmp = findRoot(Tree[x]);
        Tree[x] = tmp;
        return tmp;
    }

}

int main(){

    int n,m;

    while(~scanf("%d%d",&n,&m)){
        for(int i=0;i<n;i++){
            Tree[i]=-1;
            for(int j=0;j<n;j++){
                if(i==j)dis[i][j]=0;
                else
                    dis[i][j]=-1;
            }
        }

        for(int i=0;i<m;i++){
            int a,b,x,y;
            scanf("%d%d",&a,&b);
            x = findRoot(a);
            y = findRoot(b);
            if(x!=y){
                int dist = mod(2,i);
                for(int j=0;j<n;j++){
                    if(x==findRoot(j)){
                        for(int k=0;k<n;k++){
                            if(y==findRoot(k)){
                                dis[j][k]=dis[k][j]=(dis[j][a]+dis[b][k]+dist)%100000;
                            }
                        }
                    }
                }

                Tree[x]=y;
            }

        }

        for(int i=1;i<n;i++){
            printf("%d\n",dis[0][i]);
        }

    }



    return 0;
}

Floyd最短路算法

import java.math.*;
import java.util.*;
 
public class Main {
    static String INF="";
    static BigInteger [][]G=new BigInteger[105][105];
    static BigInteger MOD=new BigInteger("100000"),Base=new BigInteger("2");
    public static void main(String [] args){
        int n,m,i,j,k,a,b;
        for(i=0;i<160;i++) INF+="9";
        for(i=0;i<105;i++)
            for(j=0;j<105;j++)
                if(i!=j) G[i][j]=new BigInteger(INF);
                else G[i][j]=new BigInteger("0");
        Scanner in=new Scanner(System.in);
        n=in.nextInt(); m=in.nextInt();
        for(k=0;k<m;k++){
            a=in.nextInt(); b=in.nextInt();
            if(!G[a][b].toString().equals(INF)) continue;
            G[a][b]=new BigInteger(Base.pow(k).toString());
            G[b][a]=new BigInteger(Base.pow(k).toString());
        }
        for(k=0;k<n;k++)
            for(i=0;i<n;i++)
                for(j=0;j<n;j++)
                    if(G[i][j].compareTo(G[i][k].add(G[k][j]))>0)
                        G[i][j]=G[i][k].add(G[k][j]);
        for(i=1;i<n;i++) 
            if(G[0][i].toString().equals(INF))
                System.out.println("-1");
            else
                System.out.println(G[0][i].mod(MOD));
    }
}//100个点  直接弗洛伊德就能AC

参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值