图的多源点最短路问题和传递闭包之Floyd-Warshall算法 By ACReaper

我们知道求图的最短路有Dijkstra应用于无负权的算法,也有应用于有负权的Bellman0-Ford算法,但是当源点有多个呢?难道我们要调用n次的Dijkstra算法?有没有其它的算法呢?这是当然的,Floyd-Warshall就是用来解决这个问题的,也许有学过的人会说这个算法的效率太低,为O(n^3)但是,当摊销到每一条路上时,其效率为O(V)还是很高的,很实用的一个算法。


Floyd-Warshall算法是基于最短路的子路也一定是最短路这一最优子结构,加上动态规划思想来实现的,对于图中,我们假设dij(k)表示从结点Vi到结点Vj的最短路,k为其上标,表示在dij这条最短路的中间结点是属于{1,2,3....k}这个集合的,所以我们假设有结点P,集合S为{1,....k},集合Q为S-{p},那么对于dij(k)这条最短路,结点k要么在其上,要么不在,所以我们有

Dij(k) = min(Dij(k-1),Dik(k-1) + Dkj(k - 1))。

接着我们用递推来减少重复的运算,算法如下:


#include <stdio.h>
//和算法导论中Floyd-Warshall算法,在图的存储中采用邻接矩阵不同,我们采用邻接链表来
//存图,并且完成该算法

#define MAXN 1000 
#define INF 1 << 30
int first[MAXN + 1];
int next[MAXN + 1];
int d[MAXN + 1][MAXN + 1];
int c[MAXN + 1][MAXN + 1];
int u[MAXN + 1];
int v[MAXN + 1];
int w[MAXN + 1];
int n,m;
int main(){
	
	while(scanf("%d%d",&n,&m) != EOF){
		for(int i = 1; i <= n; i++){//Init,0代表NULL 
			first[i] = 0;
		}
		for(int i = 1; i <= m; i++){
			next[i] = 0;
		}
		
		for(int e = 1; e <= m; e++){//构建邻接链表 
			scanf("%d%d%d",u + e, v + e, w + e);
			next[e] = first[u[e]];
			first[u[e]] = e; 
		}
		
		//动态规划初始化 
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= n; j++){
				d[i][j] = INF;
				c[i][j] = i == j?1 : 0;
			}
			d[i][i] = 0;
			for(int e = first[i]; e != 0; e = next[e]){
				d[i][v[e]] = w[e];
				c[i][v[e]] = 1;
			}
		} 
		//从最底层开始递推 
		for(int k = 1; k <= n; k++){
			for(int i = 1; i <= n; i++){
				for(int j = 1; j <=n ; j++){
					if(d[i][k] != INF && d[k][j] != INF)
						d[i][j] <?= d[i][k] + d[k][j];
					
					c[i][j] = c[i][j] || c[i][k] && c[k][j];
				}
			}
		} 
		
		for(int i = 1; i <=n ; i++){
			for(int j = 1; j <= n; j++){
				printf("V(%d)到V(%d)的最短路存在:%d,值为:%d\n",i,j,c[i][j],d[i][j]);
			}
		}
		
	}	
	
	return 0;
}

2013 04 30

By ACReaper

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值