SHU1757 村村通工程(Floyd算法)

Description

某市启动村村通工程后,已修建了很多路,但尚未完成整个工程。不过路多了也烦恼,每次要从一个村镇到另一个村镇时,都有许多种道路方案可以选择,但走最短距离总是每个人追求的目标。

现在,已知一个村庄和另一个作为终点的村庄,请你计算出最短需要行走多少路程。

Input

本题目包含多组数据。每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有村镇的数目和已修建的道路的数目。村镇分别以0~N-1编号。

接下来是M行道路信息。每一行有三个整数A、B、X(0£A、B<N,A¹B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路。

再接下一行有两个整数S、T(0£S,T<N),分别代表起点和终点。

Output

对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1.

Sample Input

3 3
0 1 1
0 2 3
1 2 1
0 2
3 1
0 1 1
1 2

Sample Output

2
-1

Floyd算法的基本思想如下:从任意节点A到任意节点B的最短路径不外乎2种可能,1是直接从A到B,2是从A经过若干个节点X到B。所以,我们假设Dis(AB)为节点A到节点B的最短路径的距离,对于每一个节点X,我们检查Dis(AX) + Dis(XB) < Dis(AB)是否成立,如果成立,证明从A到X再到B的路径比A直接到B的路径短,我们便设置Dis(AB) = Dis(AX) + Dis(XB),这样一来,当我们遍历完所有节点X,Dis(AB)中记录的便是A到B的最短路径的距离。

根据这样的思路我写出了如下代码:
void floyd()
{
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			for(int k=0;k<n;k++)
				map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
}

愉快地交了一发,发现WA了。
仔细思考可以想到,这样写其实是错误的。
举个栗子,我们要求A—>C的最短路
A—>B—>C 为5+6=11
A—>B—>D—>C 为5+2+3=10
明显下面那种更优,但是由于
for(int k=0;k<n;k++)
     map[i][j]=min(map[i][j],map[i][k]+map[k][j])
是一个内循环,A—>C比A—>D先确定,也就无法得到下面那个正确的答案了。
那么如何解决呢?
for(int k=0;k<n;k++)
将其放在外循环。
这样一来,就是一开始只允许把0做为中转点,然后允许1,允许2……就能求到所有节点都能作为中转点的任意两点的最短路程了。
引用的别人的博客的图解,通俗易懂。 http://ahalei.blog.51cto.com/4767671/1383613

在只允许经过1和2号顶点的情况下,任意两点之间的最短路程更新为:

081029e7gjlaaul4zk7z4n.png

通过上图得知,在相比只允许通过1号顶点进行中转的情况下,这里允许通过1和2号顶点进行中转,使得e[1][3]和e[4][3]的路程变得更短了。

同理,继续在只允许经过1、2和3号顶点进行中转的情况下,求任意两点之间的最短路程。任意两点之间的最短路程更新为:

081029pd747o8o87o07o7l.png

最后允许通过所有顶点作为中转,任意两点之间最终的最短路程为:

081030h7tmht7cs2h7qftu.png


这道题的完整代码如下:
PS:如果要求路径的话可以加一条  path[i][j]=k;
#include<iostream>
#include<cstring>
#include<algorithm>
const int INF=0x3f3f3f3f;
using namespace std;
int map[1000][1000];
int n;
void floyd()
{
	for(int k=0;k<n;k++)
	    for(int i=0;i<n;i++)
		    for(int j=0;j<n;j++)
			    map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
}
int main(void)
{
	int a,b,d,m,s,t;
	while(cin>>n>>m)
	{
		memset(map,INF,sizeof(map));
		for(int i=1;i<=m;i++)
		{
			cin>>a>>b>>d;
			map[a][b]=map[b][a]=d;
		}
		cin>>s>>t;
		floyd();
		if(map[s][t]==INF)
			cout<<"-1"<<endl;
		else
		    cout<<map[s][t]<<endl;	
	}
        return 0;
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值