题目描述:
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 4
1 2
2 3
1 3
0 1
样例输出:
8
9
11
(大数mod处理)本题路径值为2^k (k最多500),结果取膜100000
由 (a+b)%m = (a%m +b%m)%m
得(2^a+2^b+·····2^k)%100000=(2^a%100000+2^b%100000 +·····2^k%100000)%100000所以得到每条路径%100000的值就行,最后相加再取膜100000就行
由a * b % m = (a % m * b % m)%m
2^( k+1)%100000=(2^k %100000 * 2%100000 )%100000 =2^k %100000*2%100000
所以要得到当前路径%100000,取上一条路径%100000的值乘2再取膜100000
但是因为保存的是取过膜的数没办法比较大小,仔细分析后发现2^k永远大于1+2^1+······+2^(k-1),后一条路会比前面所有路径的总值还大,所以直接保存这是第几条路径作为路径标号,比较时等式两边的集合中的最大路径标号所在方即为较大方,若相等比较次大的,这里可以考虑用set保存一下具体路径。
(…)原本以为求最短路径就是用Dijskstra,可是写到Dijskstra的松弛操作发现居然是一个最小生成树的题。原因就是2^k永远大于1+2^1+······+2^(k-1),那松弛的时候永远选的是标号最小的那条,那直接从最小边开始松弛,可以省去Dijskstra的 d[y]与d[x]+w[x][y]路径的大小判断。于是在生产最小生成树时,只要需要在合并根节点的时候就直接将整个图更新一下就保存了两点间的最短路径。
#include <iostream>
#include <stdio.h>
using namespace std;
#define MAXN 101
#define MAXM 501
#define MAXW 100001
int edge[MAXN][MAXN];
int n,m;
int f[MAXN];
int findF (int t){
return f[t] == t ? t : f[t] = findF(f[t]);
}
void Union(int x, int y){
int xParent = findF(x);
int yParent = findF(y);
xParent > yParent ? f[xParent] = yParent : f[yParent] = xParent;
}
int main()
{
while(cin >> n >> m){
int x,y, dis;
for(int i = 0;i < n; i ++){
f[i] = i;
edge[i][i] = 0;
}
for(int i = 0,dis = 1;i < m; i ++ ){
cin >> x >> y;
int fx = findF(x) , fy = findF(y);
if(fx != fy){
for(int j = 0; j < n; j ++){
if(fx == findF(j))
for(int k = 0; k < n; k ++){
if(fy == findF(k))
edge[j][k] = edge[k][j] = (edge[j][x] + dis + edge[y][k]) % 100000;
}
}
}
Union(x,y);
dis = dis * 2 % 100000 ;
}
for(int i=1,root = findF(0); i<n; i++)
findF(i) != root ? cout <<"-1" <<endl:cout <<edge[0][i]<<endl;
}
}