(九度教程第题)
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 < 2 m ) (2^0+2^1+,...,+2^{m-1}<2^m) (20+21+,...,+2m−1<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