【SSL】1613最短路径问题

【SSL】1613最短路径问题

Time Limit:1000MS
Memory Limit:65536K

Description

平面上有n个点(N<=100),每个点的坐标均在-10000~10000之间。其中的一些点之间有连线。若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点直线的距离。现在的任务是找出从一点到另一点之间的最短路径。

Input

输入文件short.in,共有n+m+3行,其中:
第一行为一个整数n。
第2行到第n+1行(共n行),每行的两个整数x和y,描述一个点的坐标(以一个空格隔开)。
第n+2行为一个整数m,表示图中的连线个数。
此后的m行,每行描述一条连线,由两个整数I,j组成,表示第i个点和第j个点之间有连线。
最后一行:两个整数s和t,分别表示源点和目标点。

Output

输出文件short.out仅一行,一个实数(保留两位小数),表示从S到T的最短路径的长度。

Sample Input

5
0 0 
2 0
2 2
0 2
3 1
5
1 2
1 3
1 4
2 5
3 5
1 5

Sample Output

3.41

思路

floyd

用Floyd(弗洛伊德)算法,是最简单的最短路径算法,可以计算图中任意两点间的最短路径。Floyd的时间复杂度是O (N3),适用于出现负边权的情况。
算法描述:
初始化:点u、v如果有边相连,则dis[u][v]=w[u][v]。
  如果不相连则dis[u][v]=0x7fffffff

For (k = 1; k <= n; k++)
    For (i = 1; i <= n; i++)
	 For (j = 1; j <= n; j++)
         If ((i != j) && (i != k) && (j != k)&&(dis[i][j] >dis[i][k] + dis[k][j]))
	         dis[i][j] = dis[i][k] + dis[k][j];

算法结束:dis[i][j]得出的就是从i到j的最短路径。

算法分析&思想讲解:
  三层循环,第一层循环中间点k,第二第三层循环起点终点i、j,算法的思想很容易理解:如果点i到点k的距离加上点k到点j的距离小于原先点i到点j的距离,那么就用这个更短的路径长度来更新原先点i到点j的距离。
  在上图中,因为dis[1][3]+dis[3][2]<dis[1][2],所以就用dis[1][3]+dis[3][2]来更新原先1到2的距离。
  我们在初始化时,把不相连的点之间的距离设为一个很大的数,不妨可以看作这两点相隔很远很远,如果两者之间有最短路径的话,就会更新成最短路径的长度。Floyed算法的时间复杂度是O(N3)。

dijkstra

用来计算从一个点到其他所有点的最短路径的算法,是一种单源最短路径算法。也就是说,只能计算起点只有一个的情况。
Dijkstra的时间复杂度是O (N2),它不能处理存在负边权的情况。
算法描述:
设起点为s,dis[v]表示从s到v的最短路径,pre[v]为v的前驱节点,用来输出路径。
a)初始化:dis[v]=∞(v≠s); dis[s]=0; pre[s]=0;
b)For (i = 1; i <= n ; i++)
1.在没有被访问过的点中找一个顶点u使得dis[u]是最小的。
2.u标记为已确定最短路径
3.For 与u相连的每个未确定最短路径的顶点v
if (dis[u]+w[u][v] < dis[v])
{
dis[v] = dis[u] + w[u][v];
pre[v] = u;
}
c)算法结束:dis[v]为s到v的最短距离;pre[v]为v的前驱节点,用来输出路径。

算法分析&思想讲解:
从起点到一个点的最短路径一定会经过至少一个“中转点”(例如下图1到5的最短路径,中转点是2。特殊地,我们认为起点1也是一个“中转点”)。显而易见,如果我们想求出起点到一个点的最短路径,那我们必然要先求出中转点的最短路径(例如我们必须先求出点2 的最短路径后,才能求出从起点到5的最短路径)。
换句话说,如果起点1到某一点V0的最短路径要经过中转点Vi,那么中转点Vi一定是先于V0被确定了最短路径的点。

我们把点分为两类,一类是已确定最短路径的点,称为“白点”,另一类是未确定最短路径的点,称为“蓝点”。如果我们要求出一个点的最短路径,就是把这个点由蓝点变为白点。从起点到蓝点的最短路径上的中转点在这个时刻只能是白点。
Dijkstra的算法思想,就是一开始将起点到起点的距离标记为0,而后进行n次循环,每次找出一个到起点距离dis[u]最短的点u,将它从蓝点变为白点。随后枚举所有的蓝点vi,如果以此白点为中转到达蓝点vi的路径dis[u]+w[u][vi]更短的话,这将它作为vi的“更短路径”dis[vi](此时还不确定是不是vi的最短路径)。
就这样,我们每找到一个白点,就尝试着用它修改其他所有的蓝点。中转点先于终点变成白点,故每一个终点一定能够被它的最后一个中转点所修改,而求得最短路径。

bellman-ford

简称Ford(福特)算法,同样是用来计算从一个点到其他所有点的最短路径的算法,也是一种单源最短路径算法。
能够处理存在负边权的情况,但无法处理存在负权回路的情况(下文会有详细说明)。
算法时间复杂度:O(NE),N是顶点数,E是边数。
算法实现:
设s为起点,dis[v]即为s到v的最短距离,pre[v]为v前驱。w[j]是边j的长度,且j连接u、v。
初始化:dis[s]=0,dis[v]=∞(v≠s),pre[s]=0
For (i = 1; i <= n-1; i++)
For (j = 1; j <= E; j++) //注意要枚举所有边,不能枚举点。
if (dis[u]+w[j]<dis[v])   //u、v分别是这条边连接的两个点。
{
dis[v] =dis[u] + w[j];
}

算法分析&思想讲解:
Bellman-Ford算法的思想很简单。一开始认为起点是白点(dis[1]=0),每一次都枚举所有的边,必然会有一些边,连接着白点和蓝点。因此每次都能用所有的白点去修改所有的蓝点,每次循环也必然会有至少一个蓝点变成白点。

代码

floyd

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,dx,dy;
double a[110][110],x[110],y[110];
void input()
{
	int i,j;
	memset(a,0x7f7f7f,sizeof(a));
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%lf%lf",&x[i],&y[i]);//保存坐标 
		a[i][i]=0;
	}
	scanf("%d",&m);
	for(i=0;i<m;i++)
	{
		scanf("%d%d",&dx,&dy);
		a[dx][dy]=a[dy][dx]=sqrt((x[dx]-x[dy])*(x[dx]-x[dy])+(y[dx]-y[dy])*(y[dx]-y[dy]));//计算距离
	}
	scanf("%d%d",&dx,&dy);
	return;
}
void floyed()//计算最短路 
{
	int i,j,k,w;
	for(k=1;k<=n;k++)
		for(i=1;i<n;i++)
			for(j=i+1;j<=n;j++)
				if(a[i][j]>a[i][k]+a[k][j])
					a[i][j]=a[j][i]=a[i][k]+a[k][j];
	return;
}
int main()
{
	input();
	floyed();
	printf("%.2lf",a[dx][dy]);
	return 0;
}

dijkstra

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,dx,dy;
double a[110][110],x[110],y[110],dis[3010];
bool d[3010];
void input()
{
	int i,j;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		for(j=i+1;j<=n;j++)
			a[i][j]=a[j][i]=100000000;
	for(i=1;i<=n;i++)
	{
		scanf("%lf%lf",&x[i],&y[i]);//保存坐标 
		a[i][i]=0;
	}
	scanf("%d",&m);
	for(i=0;i<m;i++)
	{
		scanf("%d%d",&dx,&dy);
		a[dx][dy]=a[dy][dx]=sqrt((x[dx]-x[dy])*(x[dx]-x[dy])+(y[dx]-y[dy])*(y[dx]-y[dy]));//计算距离
	}
	scanf("%d%d",&dx,&dy);
	return;
}
void DIJ()//计算单源最短路 
{
	int i,j,mn,w;
	memset(d,0,sizeof(d));
	for(i=1;i<=n;i++)
	{
		a[i][i]=0;
		dis[i]=a[dx][i];
	}
	dis[dx]=0;
	d[dx]=1;
	for(i=1;i<n;i++)
	{
		mn=10000000;
		for(j=1;j<=n;j++)//找最短蓝点 
			if(!d[j]&&dis[j]<mn)
			{
				w=j;
				mn=dis[j];
			}
		d[w]=1;
		for(j=1;j<=n;j++)//松弛 
			if(!d[j]&&dis[w]+a[w][j]<dis[j])
				dis[j]=dis[w]+a[w][j];
	}
	return;
}
int main()
{
	input();
	DIJ();
	printf("%.2lf",dis[dy]);
	return 0;
}

ford

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,dx,dy;
double x[110],y[110],dis[110];
struct jgt
{
	int x,y;
	double s;
}b[10000]; 
void input()
{
	int i,j;
	scanf("%d",&n);
	dis[0]=100000000;
	for(i=1;i<=n;i++)
	{
		scanf("%lf%lf",&x[i],&y[i]);//保存坐标
		dis[i]=100000000;
	}
	scanf("%d",&m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&b[i].x,&b[i].y);
		b[i].s=sqrt((x[b[i].x]-x[b[i].y])*(x[b[i].x]-x[b[i].y])+(y[b[i].x]-y[b[i].y])*(y[b[i].x]-y[b[i].y]));//计算距离
	}
	scanf("%d%d",&dx,&dy);
	return;
}
void ford()//计算最短路 
{
	int i,j,k,w;
	bool y=1;
	dis[dx]=0;
	for(i=1;y&&i<n;i++)
	{
		y=0;
		for(j=1;j<=m;j++)//扫描每一条边 
		{
			if(dis[b[j].x]+b[j].s<dis[b[j].y])dis[b[j].y]=dis[b[j].x]+b[j].s,y=1;//松弛 
			if(dis[b[j].y]+b[j].s<dis[b[j].x])dis[b[j].x]=dis[b[j].y]+b[j].s,y=1;
		}	
	}
	return;
}
int main()
{
	input();
	ford();
	printf("%.2lf",dis[dy]);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值