问题 C: 最短路径
时间限制: 1 Sec 内存限制: 32 MB
提交: 169 解决: 29
题目描述
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 的结果输出。
样例输入
4 3 0 1 1 2 2 0
样例输出
1 3 -1
经验总结
这一题......显然有坑呐!第K条道路长度为2^K,第32条开始就超过整型的表示范围了!所以,这题需要用一定的技巧。
第一个技巧是快速幂,这个写一个小函数就能实现了,用于对大型指数取模运算。
第二个,就是你要看到这个题目,是有规律的,每条依次出现的道路,后面的道路必然大于前面出现的所有道路的和,
即2^1+2^2+.......+2^(n-1)=(2^n) - 1< 2^n 所以,后面输入的边需要进行判断:
1 如果这两个端点已经连通了,那么这个输入的边必然大于原来连通的距离,所以不需要更新
2 如果这两个端点仍没有任何路径可达,就更新这两个端点所有可达点的路径,这两个端点将并入一个连通图中。
综上所述,这一题需要结合并查集和快速幂,即可正确运行~~
正确代码
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=110;
const int INF=-1;
int father[maxn],d[maxn][maxn];
int n;
int QuickPow(int a,int b,int m)
{
long long x=a;
int ans=1;
while(b>0)
{
if(b&1)
{
ans=ans*x%m;
}
x=x*x%m;
b=b>>1;
}
return ans;
}
void init(int n)
{
for(int i=0;i<n;++i)
{
father[i]=i;
d[i][i]=0;
}
}
int findFather(int a)
{
while(a!=father[a])
a=father[a];
return a;
}
int main()
{
int m,u,v;
while(~scanf("%d %d",&n,&m))
{
fill(d[0],d[0]+maxn*maxn,INF);
init(n);
for(int k=0;k<m;++k)
{
scanf("%d %d",&u,&v);
int x=findFather(u);
int y=findFather(v);
if(x!=y)
{
int num=QuickPow(2,k,100000);
for(int i=0;i<n;++i)
{
if(x==findFather(i))
{
for(int j=0;j<n;++j)
{
if(y==findFather(j))
{
d[i][j]=d[j][i]=(num+d[i][u]+d[j][v])%100000;
}
}
}
}
}
father[y]=x;
}
for(int i=1;i<n;++i)
printf("%d\n",d[0][i]);
}
return 0;
}