最短路径问题【Floyed、Dijkstra、Ford、SPFA】 SSL 1613
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
解题思路–
Floyed-Warshall(弗洛伊德)算法
Dijkstra算法
Bellman-Ford(福特)算法
SPFA算法
注意事项:
- 勾股定理:
a^2 + b^2 = c^2
代码–
方法一
Floyed(弗洛伊德)算法
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
struct emm
{
int x,y;
}a[110];
double b[110][110];
int l,ll,s,t,n,m;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d%d",&a[i].x,&a[i].y);
scanf("%d",&m);
memset(b,0x7f,sizeof(b));
for (int i=1;i<=m;++i)
{
scanf("%d%d",&l,&ll);
b[l][ll]=b[ll][l]=sqrt(pow(double(a[l].x-a[ll].x),2)+pow(double(a[l].y-a[ll].y),2));//勾股定理
}
scanf("%d%d",&s,&t);//求从s到t的最短路径
for (int k=1;k<=n;++k)//Floyed
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
if (k!=i && k!=j && i!=j)
if (b[i][j]>b[i][k]+b[k][j])
b[i][j]=b[i][k]+b[k][j];
printf("%.2lf\n",b[s][t]);
return 0;
}
方法二
Dijkstra算法
#include<cstdio>
#include<iostream>
#include<cmath>
const double hhg=75000;
struct emm
{
int x,y;
}a[101];
int n,m,s,t,o,l,ll;
bool f[101];
double b[101][101],c[101],yyg;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d%d",&a[i].x,&a[i].y);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
b[i][j]=hhg;//手动初始化
scanf("%d",&m);
for (int i=1;i<=m;++i)
{
scanf("%d%d",&l,&ll);
b[l][ll]=b[ll][l]=sqrt(pow(double(a[l].x-a[ll].x),2)+pow(double(a[l].y-a[ll].y),2));//勾股定理
}
scanf("%d%d",&s,&t);//求从s到t的最短路径
for (int i=1;i<=n;++i) c[i]=b[s][i];//Dijkstra
f[s]=true,c[s]=0;//标记f[s]为“已求出”;c[s]到自己的最短距离是0
for (int i=1;i<n;++i)
{
yyg=hhg,o=0;//yyg放最大值;o清零;
for (int j=1;j<=n;++j)
if (!f[j] && c[j]<yyg)
yyg=c[j],o=j;//yyg存现阶段的最小值,o存最小值的位置
if (o==0) break;
f[o]=true;//标记
for (int j=1;j<=n;++j)
if (c[j]>c[o]+b[o][j] && !f[j])//判断是否可更新
c[j]=c[o]+b[o][j];//更新
}
printf("%.2lf\n",c[t]);
}
方法三
Bellman-Ford(福特)算法
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
struct emm
{
int x,y;
}a[101];
double b[1001],c[101];
bool h[101];
int l[1001],ll[1001],n,m,s,t;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d%d",&a[i].x,&a[i].y);
scanf("%d",&m);
for (int i=1;i<=m;++i)
{
scanf("%d%d",&l[i],&ll[i]);
b[i]=sqrt(pow(double(a[l[i]].x-a[ll[i]].x),2)+pow(double(a[l[i]].y-a[ll[i]].y),2));//勾股定理
}
scanf("%d%d",&s,&t);//求从s到t的最短路径
memset(c,0x7f,sizeof(c));//Ford
c[s]=0;//c[s]到自己的最短距离是0
for (int i=1;i<n;i++)
{
bool hfy=false;//判断更新是否结束
for (int j=1;j<=m;j++)
{
if (c[l[j]]+b[j]<c[ll[j]])//
{ //
c[ll[j]]=c[l[j]]+b[j];//双向判断更新
hfy=true; //
} //
if (c[ll[j]]+b[j]<c[l[j]])//
{
c[l[j]]=c[ll[j]]+b[j];
hfy=true;
}
}
if (!hfy) break;
}
printf("%.2lf",c[t]);
return 0;
}
方法四
SPFA算法
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
struct emm
{
int x,next;
double y;
}f[2001];
double dis[101];
bool pd[101];
int a[101][2],ls[101],bty[2001];
int n,m,ll,lr,s,t,l,r=1,u,o,q;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d%d",&a[i][0],&a[i][1]);
scanf("%d",&m);
for (int i=1;i<=m;++i)
{
scanf("%d%d",&ll,&lr);
f[++t].x=lr; f[t].next=ls[ll]; ls[ll]=t;//邻接表
f[t].y=sqrt(pow(double(a[ll][0]-a[lr][0]),2)+pow(double(a[ll][1]-a[lr][1]),2));//勾股定理
f[++t].x=ll; f[t].next=ls[lr]; ls[lr]=t;
f[t].y=f[t-1].y;
}
scanf("%d%d",&s,&q);
memset(dis,0x7f,sizeof(dis));
dis[s]=0,bty[1]=s,pd[s]=true;
while (l<r)
{
l++,u=bty[l];//头指针向下移一位,取出指向的点l
pd[u]=false;//把处理过的点退出标记
for (int i=ls[u];i;i=f[i].next)
if (dis[o=f[i].x]>dis[u]+f[i].y)//判断此点是否可更新(是↓)
{
dis[o]=dis[u]+f[i].y;
if (!pd[o])//判断此点在不在队列里面(不在↓)
{
pd[o]=true;//标记此点在队列中
r++;
bty[r]=o;//加入队列
}
}
}
printf("%.2lf",dis[q]);
return 0;
}