输入:
本题目包含多组数据,请处理到文件结束。
每组数据第一行包含两个正整数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),分别代表起点和终点。
每组数据第一行包含两个正整数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),分别代表起点和终点。
输出:
对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1
用于解决最短路径问题的算法被称做“最短路径算法”, 有时被简称作“路径算法”最常用的路径算法有四种:
1.Dijkstra(迪杰斯特拉)------------解决单源最短路问题。(贪心的思想)----条件:非负权值。
2.Bellman-Ford(贝尔曼-福特)--------解决单源最短路问题。(逐个遍历每一条边)
3.Floyd(弗洛伊德)---------------解决全源最短路问题。(dp的思想)
4.SPFA(Shortest Path Faster Algorithm)-----------解决单元最短路问题。(队列实现,是bellman-Ford算法的一种改进)
方法一:bellmanford,
Bellman-Ford算法可以大致分为三个部分
第一,初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。
第二,进行循环,循环下标为从1到n-1(n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。
第三,遍历途中所有的边(edge(u,v)),判断是否存在这样情况:
d(v) > d (u) + w(u,v)
则返回false,表示途中存在从源点可达的权为负的回路。
第一,初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。
第二,进行循环,循环下标为从1到n-1(n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。
第三,遍历途中所有的边(edge(u,v)),判断是否存在这样情况:
d(v) > d (u) + w(u,v)
则返回false,表示途中存在从源点可达的权为负的回路。
Bellman-Ford算法可以解决图中有权为负数的边的单源最短路径问题。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 999999
int map[205][205];
int vis[205],dis[205];
int n,m;
int s,t;
void bellmanford()//美国数学家理查德•贝尔曼(Richard Bellman, 动态规划的提出者)和小莱斯特•福特(Lester Ford)发明
{
int i,j,k;
for(i=0; i<n; i++)
dis[i]=map[s][i];//dis[]数组初始化,以s点为源点,向外扩散
for(i=1; i<n; i++) //再循环n-1次
{
for(j=0; j<n; j++)
{
if(j!=s)//除去源点
{
for(k=0; k<n; k++)
{
if(dis[k]>dis[j]+map[j][k])//此步的意思是从源点到各点的最小距离,路径经过所取过的点
dis[k]=dis[j]+map[j][k];
}
}
}
}
if(dis[t]!=M)
printf("%d\n",dis[t]);
else
printf("-1\n");
}
int main()
{
int i,j;
int a,b,c;
while(~scanf("%d%d",&n,&m))
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
map[i][j]=M;
map[i][i]=0;
}
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
if(map[a][b]>c)//这一步必须有,因为可能输入的点相同但距离不同,要取最小的那一个
map[a][b]=map[b][a]=c;
}
scanf("%d%d",&s,&t);
bellmanford();
}
return 0;
}
方法二:迪杰斯特拉算法 ------遍历经过的节点多,效率低---------要非负权值
1.从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;
2.更新与i直接相邻顶点的dist值。(dist[j]=min{dist[j],dist[i]+matrix[i][j]})
3.知道U=V,停止。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 999999
int map[205][205];
int vis[205],dis[205];
int n,m;
int s,t;
void Dijkstra()//迪杰斯特拉算法
{
int i,j;
int min,flag;
for(i=0; i<n; i++)//此处从0还是1开始,要看点是从几开始编号的
dis[i]=map[s][i];
vis[s]=1;//源点
for(i=1; i<n; i++)
{
min=M;
for(j=0; j<n; j++)
{
if(!vis[j]&&dis[j]<min)//取最小的距离
{
min=dis[j];
flag=j;
}
}
vis[flag]=1;
for(j=0; j<n; j++)
{
if(!vis[j]&&dis[j]>dis[flag]+map[flag][j])//重点之处
dis[j]=dis[flag]+map[flag][j];
}
}
if(dis[t]!=M)
printf("%d\n",dis[t]);
else
printf("-1\n");
}
int main()
{
int i,j,a,b,c;
while(~scanf("%d%d",&n,&m))
{
memset(vis,0,sizeof(vis));//初始化归零,必须有
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
map[i][j]=M;
map[i][i]=0;//此处必须有
}
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
if(map[a][b]>c)//必须有,选取最小值
map[a][b]=map[b][a]=c;//表明是无向图
}
scanf("%d%d",&s,&t);
Dijkstra();
}
return 0;
}
方法三:Floyd(弗洛伊德)真不是很懂。
Floyd-Warshall算法的原理是动态规划。
设Di,j,k为从i到j的只以(1..k)集合中的节点为中间节点的最短路径的长度。
- 若最短路径经过点k,则Di,j,k = Di,k,k − 1 + Dk,j,k − 1;
- 若最短路径不经过点k,则Di,j,k = Di,j,k − 1。
因此,Di,j,k = min(Di,k,k − 1 + Dk,j,k − 1,Di,j,k − 1)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 9999999
int map[205][205];
int n,m;
int s,t;
void Floyd()
{
int i,j,k;
for(k=0; k<n; k++)
for(i=0; i<n; i++)
for(j=0;j<n;j++)
if(map[i][k]!=M&&map[k][j]!=M&&map[i][j]>map[i][k]+map[k][j])
map[i][j]=map[i][k]+map[k][j];
if(map[s][t]!=M)
printf("%d\n",map[s][t]);
else
printf("-1\n");
}
int main()
{
int i,j;
int a,b,c;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
map[i][j]=M;
map[i][i]=0;
}
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
if(map[a][b]>c)
map[a][b]=map[b][a]=c;
}
scanf("%d%d",&s,&t);
Floyd();
}
return 0;
}
方法四:
就是在Bellman_Ford算法的基础上加上队列实现。
队列中的元素出队后还可以再进队。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 9999999
int map[205][205],q[205];
int dis[205],vis[205];
int n,m;
int s,t;
void SPFA()
{
int i,p;
int jin,chu;
jin=chu=0;
for(i=0; i<n; i++)
dis[i]=M;
dis[s]=0;
q[jin++]=s;
vis[s]=1;
while(chu<jin)
{
p=q[chu++];
for(i=0; i<n; i++)
{
if(dis[i]>dis[p]+map[p][i])
{
dis[i]=dis[p]+map[p][i];
if(!vis[i])
{
q[jin++]=i;
vis[i]=1;
}
}
}
vis[p]=0;
}
if(dis[t]!=M)
printf("%d\n",dis[t]);
else
printf("-1\n");
}
int main()
{
int i,j;
int a,b,c;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(vis,0,sizeof(vis));
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
map[i][j]=M;
map[i][i]=0;
}
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
if(map[a][b]>c)
map[a][b]=map[b][a]=c;
}
scanf("%d%d",&s,&t);
SPFA();
}
return 0;
}