题意:
给你一张无向图,求从S到E恰好经过N条边(可重复走)的最短路。
分析:
(1):根据Floyd算法的特殊性,它是通过插入点的方法来找到最短路的,特别适合此题,假如我们插入N-1个点,求到的则是经过N条边的最短路。假设我们每一次floyd只插入一个点的话,那么单步Floyd N次的话,则可以插入N个点。
(2):而N次floyd可以用倍增思想加速,就是自底向上的二分。类似求矩阵A^N。
(3):这里还有一点要注意,T的范围是(2~100),所以最多顶点只有200,而顶点标号的范围却(1 ≤ I1i ≤ 1,000; 1 ≤ I2i ≤ 1,000)。这样我们可以将编号离散化,开map时只需开到map[205][205],既节省了内存,又使所以结点的编号连续,加快了速度。
(4):还有一点比较郁闷,一开始INF是定义成99999999,不停的WA,改成100000001就AC了。。。囧。。。。。看来以后一定要往大开。。。。不然怎么死都不知道。。。唉
源代码:
/*Floyd(倍增法)*/ /*N次Floyd*/ /*求从S到E恰好经过N条边(可重复走)的最短路。*/ /*AC代码:125ms*/ #include <iostream> #define MAXN 210 #define INF 1000000000//注意,原来写99999999会WA!!! int v[1005];//存那些牛的顶点(离散化) int map[MAXN][MAXN];//存原地图 int ans[MAXN][MAXN];//存答案 int temp[MAXN][MAXN];//临时储存 int cnt; int N,T,S,E; void Init() { int val,s,e,i,j; for(i=0;i<205;i++)//因为离散化后最多只有200个点 { for(j=0;j<205;j++) map[i][j]=ans[i][j]=temp[i][j]=INF; ans[i][i]=0; } cnt=0; memset(v,0,sizeof(v)); for(i=1;i<=T;i++) { scanf("%d%d%d",&val,&s,&e); if(v[s]==0)//连续化结点编号,提高运行效率 { cnt++; v[s]=cnt; } if(v[e]==0) { cnt++; v[e]=cnt; } if(map[v[s]][v[e]]>val) map[v[s]][v[e]]=map[v[e]][v[s]]=val; } } void floyd(int a[MAXN][MAXN],int b[MAXN][MAXN],int c[MAXN][MAXN]) { int i,j,k; for(k=1;k<=cnt;k++) for(i=1;i<=cnt;i++) for(j=1;j<=cnt;j++) if(a[i][j]>b[i][k]+c[k][j]) a[i][j]=b[i][k]+c[k][j]; } void copy(int a[MAXN][MAXN],int b[MAXN][MAXN]) { int i,j; for(i=1;i<=cnt;i++) for(j=1;j<=cnt;j++) { a[i][j]=b[i][j]; b[i][j]=INF; } } void work(int n)//N次Floyd { while(n) { if(n&1) { floyd(temp,ans,map);//多出来的1先加到ans copy(ans,temp); } floyd(temp,map,map); copy(map,temp); n>>=1; } } int main() { while(scanf("%d%d%d%d",&N,&T,&S,&E)!=EOF) { Init(); work(N); printf("%d\n",ans[v[S]][v[E]]); } return 0; }