在求最短路时,通过深搜和宽搜可以解决,但是使用Floyd-Warshall会有更高的效率
只用五行代码求最短路径!!!
案例:a城市到b城市有许多公路,有些城市则没有,为了节约经费,求出a到b的最短路程
以下为例,两地路程用邻接矩阵表示
试想通过以往的经验,如果任意两点(例如顶点a到顶点b)之间的距离路程缩短,只能引入第三个点(顶点k)并通过k中转即a→k→b,才能缩短a到b的路程,那么中转1~n的哪一个点呢?甚至有时候不止通过一个点,而是经过两个点或者多个点让路程变得更短。
下面我们来将这个问题优化:
假如允许经过1号顶点时,只需要判断e[i][1]+e[1][i]
是否比e[i][j]
要小即可,代码实现代码实现:
for(int i=1;i<=n;i++){
for(int j=1j<=n;j++){
if(e[i][1]+e[1][j]<e[i][j]){
e[i][j]=e[i][1]+e[1][j];
}
}
}
任意两点之间最短路程更新为:
在允许经过1和2号顶点情况下,代码实现如下:
//经过1号顶点时
for(int i=1;i<=n;i++){
for(int j=1j<=n;j++){
if(e[i][1]+e[1][j]<e[i][j]){
e[i][j]=e[i][1]+e[1][j];
}
}
}
//经过2号顶点时
for(int i=1;i<=n;i++){
for(int j=1j<=n;j++){
if(e[i][2]+e[2][j]<e[i][j]){
e[i][j]=e[2][1]+e[2][j];
}
}
}
在允许经过1和2号顶点情况下,任意两点之间最短;路程更新为:
在允许经过1和2、3号顶点情况下,任意两点之间最短路程更新为:
最后允许所有点中转,最短路程更新为:
代码实现:
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1j<=n;j++){
if(e[i][k]+e[k][j]<e[i][j]){
e[i][j]=e[i][k]+e[k][j];
}
}
}
}
这段代码的基本思想是:最开始允许经过1号顶点进行,求任意两点最短距离中转,接下来只允许经过2号顶点进行中转…允许1~n号顶点进行中转
用一句话概括就是:从i号顶点到j号顶点只经过前k号顶点的最短路径,其实是一种动态规划的思想
完整代码:
求a地到b地的最短路程
#include<cstdio>
int e[201][201],n,m,t1,t2,t3,a,b;
int main(){
scanf("%d %d %d %d",&n,&m,&a,&b);
//初始化矩阵
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j){
e[i][j]=0;
}else{
e[i][j]=99999999;
}
}
}
//读入边
for(int i=1;i<=m;i++){
scanf("%d %d %d",&t1,&t2,&t3);
//printf("第%d组\n",i);
e[t1][t2]=t3;
}
//Floyd-Warshall算法核心代码
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(e[i][k]+e[k][j]<e[i][j]){
e[i][j]=e[i][k]+e[k][j];
}
}
}
}
printf("===============最短路程====================\n");
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%d ",e[i][j]);
}printf("\n");
}
printf("a地到b地最短路程为:%d\n",e[a][b]);
return 0;
}
注意:-Floyd-Warshall算法可以处理带有负权边(边的值为负数)的图,
但不能处理带有负权回路(或者叫“负权环”)的图