POJ--3613[Cow Relays] floyd 倍增法

 

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


 

你可能感兴趣的:(POJ--3613[Cow Relays] floyd 倍增法)