欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

5行代码搞定floyd算法

程序员文章站 2022-04-06 18:46:25
...

简介

floyd是图搜算法中很经典的一个算法,用于求一副图中任意两点之间的最短路径(时间,花费等)。其算法思想感觉比Dijkstra简单,而且代码也很容易实现。不过就是效率比较低,三个for循环导致复杂度为O(n3)。

实例

假如有如下的地图,图中四个点代表不同的城市,带箭头的边表示各城市间的航线(城市1可以飞到城市2,但城市2不可直接飞到城市1,只能通过其他城市周转),航线附近的数字为机票价格。
5行代码搞定floyd算法
我们利用二维矩阵存储这幅地图的各个城市和每条航线的信息。
比如1号城市到2号城市的机票为2元,则设graph[1][2]的值为2。2号城市无法到达4号城市,则设置graph[2][4]的值为∞(自己设定一个较大的值即可)。另外此处约定一个城市自己飞到自己的票价为0,例如graph[1][1]为0,具体如下:
5行代码搞定floyd算法
那么如何求任意城市之间最少的机票开销呢?
其实思想很简单,就是通过一个中间城市周转一下。例如从城市1直接飞到城市3需要6元,但是可以选择先从城市1飞到城市2(2元),再从城市2飞到城市3(3元),这样只需要花费2+3=5元即可。
同理,从城市4直接飞到城市3机票需要12元,而可以选择先从4飞到1(5元),再从1飞到3(6元),花费降到5+6=11元。为了省1块钱,旅途要波折一点。
如果为了丧心病狂地省钱,不怕麻烦,甚至可以先从城市4飞到城市1,然后从1飞到2,再从2飞到3,这样只需要5+2+3=10块钱,比4直飞3的12块整整省了2元。

算法思想

所以,如果要让任意两个城市(例如从顶点a点到顶点b)之间的开销变少,只能引入第三个城市(顶点k),并通过这个顶点k中转即a->k->b,才可能缩短原来从顶点a点到顶点b的开销。那么这个中转的顶点k是1~n中的哪个点呢?甚至有时候不只通过一个点,而是经过两个点或者更多点中转开销则会更少,即a->k1->k2->b或者a->k1->k2…->ki->……->b。

如果现在只允许经过1号城市,求任意两个城市之间的最低票价,应该如何求呢?只需判断graph[i][1]+graph[1][j]是否比graph[i][j]要小即可。graph[i][j]表示的是从i号城市到j号城市之间的机票花费。graph[i][1]+graph[1][j]表示的是从i号城市先到1号城市,再从1号城市到j号城市的票价之和。其中i是1~n循环,j也是1~n循环,代码实现如下:

for(i = 1;i <= n;i++) {   
    for(j = 1;j <= n;j++) {   
        if ( graph[i][j] > graph[i][1] + graph[1][j] )   
            graph[i][j] = graph[i][1] + graph[1][j];   
    }   
} 

在只允许通过1号城市周转的情况下,任意两个城市之间的最低机票花费更新为:
5行代码搞定floyd算法
由上图可知:在只通过1号城市中转的情况下,3号城市到2号城市(graph[3][2])、4号城市到2号城市(graph[4][2])以及4号城市到3号城市(graph[4][3])的票价开销都变少了。

接下来继续求在只允许经过1和2号两个城市中转的情况下任意两点之间的最低票价。此时只需要在经由1号城市周转的结果之下,加入2号城市,再判断如果经过2号城市是否可以使得i号城市到j号城市之间的票价变得更少。即判断graph[i][2]+graph[2][j]是否比graph[i][j]要小,代码实现为如下:

//经过1号顶点   
for(i=1;i<=n;i++)   
    for(j=1;j<=n;j++)   
         if (graph[i][j] > graph[i][1]+graph[1][j])
               graph[i][j]=graph[i][1]+graph[1][j];   
//经过2号顶点   
for(i=1;i<=n;i++)   
    for(j=1;j<=n;j++)   
         if (graph[i][j] > graph[i][2]+e[2][j])  
             graph[i][j]=graph[i][2]+graph[2][j]; 

所以依次类推,只需要在最外层循环中,不断的增加可以中转的城市即可:

//多源最短路径 floyd算法(c/c++)
void floyd(int graph[][5],int points_num) {
        for(int k=1;k<=points_num;k++)   
            for(int i=1;i<=points_num;i++)   
                 for(int j=1;j<=points_num;j++)   
                     if(graph[i][j]>graph[i][k]+graph[k][j])   
                          graph[i][j]=graph[i][k]+graph[k][j]; 
}

完整测试代码

注意矩阵从(1,1)元素开始

算法定义

define INF 999999   //定义无穷大值
#define POINTS 100    //定义城市数(顶点数)
#define EDGES 100     //定义航线数(路径数)

//创建图
void createGraph(int graph[][POINTS]) {
    int mid_tmp,
        points_num,
        edges_num;
    //获取点数 & 边数
    cout<<"请输城市数量,航线数量"<<endl;
    scanf("%d %d",&points_num,&edges_num);

    //全部初始化为正无穷
    for (int i = 1;i <= points_num;++i)
        for (int j = 1;j <= points_num;++j) {
                if (i == j)
                    graph[i][j] = 0;
                else
                    graph[i][j] = INF;
        }

    cout<<"请输入各城市间的航线 ,票价"<<endl;
    //输入边
    int m,n,length;
    for (int i = 1;i <= edges_num;++i) {
        scanf("%d %d %d",&m,&n,&length);
        graph[m][n] = length;
    }
}

//多源最短路径 floyd算法
void floyd(int graph[][POINTS],int points_num) {
    for(int k=1;k<=points_num;k++)   
        for(int i=1;i<=points_num;i++)   
            for(int j=1;j<=points_num;j++)   
                if(graph[i][j]>graph[i][k]+graph[k][j])   
                    graph[i][j]=graph[i][k]+graph[k][j];
}

//输出最终的结果  
void showGraph(int graph[][POINTS],int n) {

    for(int i=1;i<=n;i++){    
        for(int j=1;j<=n;j++) {   
            printf("%10d",graph[i][j]);   
        }   
        printf("\n");   
    }
}    

主函数

int main(void) {
    int graph[POINTS][POINTS];
    createGraph(graph);
    floyd(graph,4);

    cout<<"任意城市最低票价图:"<<endl;
    showGraph(graph,4);
    system("pause");

    return 0;
}

运行截图

5行代码搞定floyd算法

转载自:https://blog.csdn.net/lhj884/article/details/47304481

相关标签: 最短路径