题意:
给你一张无向图,求从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;
- }