最短路徑問題--Floyd多源最短路徑算法

Dijkstra和Bellman_Ford都是從一個起點出發,計算到各頂點的距離。不過有時候需要求對所有成對定點的最短距離。引入了Floyd算法。

Floyd算法考慮的是一條最短路徑上的中間結點。假設圖中有一個定點x,對於u到v的最短路徑,該路徑可能經過x,也可能不經過x。設所有結點集合爲S
(1)、不經過x:那麼該路徑只會經過S-{x}中的結點。
(2)、經過x:此時,將路徑分爲(u,x)和(x,v)。且這兩個路徑也是最短路徑。很顯然這兩個路徑也不會經過x,故也只將S-{x}最爲途徑點。

將圖中所有點S考慮在內,從u到v的最短路徑肯定是上面兩種情況中路徑最小的一個。
故:dist(u,v,S) = min ( dist(u,v,S-{x}), dist(u,x,S-{x})+dist(x,v,S-{x}) )

對上式進行簡單的修改,設S(k)={0,1,2,。。。,k},表示總的結點,dist(u,v,k)表示考慮第0第到k個結點的u到v最短路徑。則上式可以寫爲:dist(u,v,k) = min ( dist(u,v,k-1), dist(u,k,k-1)+dist(k,v,k-1) ).(k表示第k個結點)
進一步簡化:我們可知dist(u,k,k-1)是考慮起點到第k-1個頂點,計算u到k的最短路徑,dist(u,k,k)是考慮起點到第k個頂點,計算u到k的最短路徑。我們看到由於這兩個路徑中起點或終點有一個爲k,在將k考慮爲是否經過沒有意義。因此式子可以寫成:
dist(u,v) = min ( dist(u,v), dist(u,k)+dist(k,v) ).

通過遞歸調用即可求得最終的dist(u,v).且在計算過程中可以保存k值,即經過的點,用來還原最短路徑。

C++代碼如下:

#include <iostream>
#include <vector>
#define INF 999999
using namespace std;

void floyd(int n, int adj[][7], int via[][7])
{
    for(int k=0; k<n; k++)
    {
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                if(adj[i][j]>adj[i][k]+adj[k][j])
                {
                    via[i][j]=k;
                    adj[i][j]=adj[i][k]+adj[k][j];
                }
            }
        }
    }
}

//計算a到b的最短路徑,保存在path中
void reconstruct(int a, int b, vector<int>& path, int via[][7])
{
    if(via[a][b]==-1)
    {
        path.push_back(a);
        if(a!=b)
            path.push_back(b);
    }
    else
    {
        int w=via[a][b];
        reconstruct(a,w,path,via);
        path.pop_back();
        reconstruct(w,b,path,via);
    }
}
int main()
{
    //v:結點個數,m:邊數
    int v=7, m=9;
    //邊之間的權值,沒有邊相連權值爲無窮大
    int adj[7][7];
    //存儲途經點
    int via[7][7];
    //初始化爲無窮大
    memset(adj,INF,sizeof(adj));
    //初始化爲-1
    memset(via,-1,sizeof(via));
    for(int i=0; i<v; i++)
    {
        //自身到自身的距離爲0
        adj[i][i]=0;
    }
    for(int i=0; i<m; i++)
    {
        int a=0,b=0,c=0;
        cin>>a>>b>>c;
        adj[a-1][b-1]=c;
        adj[b-1][a-1]=c;
    }
    floyd(v, adj, via);
    vector<int> path;
    reconstruct(0, 5, path,via);
    for(vector<int>::iterator iter=path.begin(); iter!=path.end(); iter++)
    {
        cout<<(*iter)+1<<" ";
    }
    cout<<endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章