1. 程式人生 > >codeup 問題 B: 演算法7-16:弗洛伊德最短路徑演算法

codeup 問題 B: 演算法7-16:弗洛伊德最短路徑演算法

                                 問題 B: 演算法7-16:弗洛伊德最短路徑演算法

                                                               時間限制: 1 Sec  記憶體限制: 32 MB                                                                           提交: 107  解決: 66                                                               [

提交][狀態][討論版][命題人:外部匯入]

題目描述

在帶權有向圖G中,求G中的任意一對頂點間的最短路徑問題,也是十分常見的一種問題。解決這個問題的一個方法是執行n次迪傑斯特拉演算法,這樣就可以求出每一對頂點間的最短路徑,執行的時間複雜度為O(n3)。而另一種演算法是由弗洛伊德提出的,時間複雜度同樣是O(n3),但演算法的形式簡單很多。可以將弗洛伊德演算法描述如下:

在本題中,讀入一個有向圖的帶權鄰接矩陣(即陣列表示),建立有向圖並按照以上描述中的演算法求出每一對頂點間的最短路徑長度。

輸入

輸入的第一行包含1個正整數n,表示圖中共有n個頂點。其中n不超過50。

以後的n行中每行有n個用空格隔開的整數。對於第i行的第j個整數,如果大於0,則表示第i個頂點有指向第j個頂點的有向邊,且權值為對應的整數值;如果這個整數為0,則表示沒有i指向j的有向邊。當i和j相等的時候,保證對應的整數為0。

輸出

共有n行,每行有n個整數,表示源點至每一個頂點的最短路徑長度。如果不存在從源點至相應頂點的路徑,輸出-1。對於某個頂點到其本身的最短路徑長度,輸出0。

請在每個整數後輸出一個空格,並請注意行尾輸出換行。

樣例輸入

4
0 3 0 1
0 0 4 0
2 0 0 0
0 0 1 0

樣例輸出

0 3 2 1 
6 0 4 7 
2 5 0 3 
3 6 1 0 

提示

在本題中,需要按照題目描述中的演算法完成弗洛伊德演算法,並在計算最短路徑的過程中將每個頂點是否可達記錄下來,直到求出每一對頂點的最短路徑之後,演算法才能夠結束。

相對於迪傑斯特拉演算法,弗洛伊德演算法的形式更為簡單。通過一個三重迴圈,弗洛伊德演算法可以方便的求出每一對頂點間的最短距離。

另外需要注意的是,為了更方便的表示頂點間的不可達狀態,可以使用一個十分大的值作為標記。而在題目描述中的演算法示例使用了另外一個三維陣列對其進行表示,這使原本的O(n3)時間複雜度增長到了O(n4),這也是需要自行修改的部分。

解題思路:此題是全源最短路徑問題,用之前的dijkstra演算法解決的話要執行n次,雖然到最後複雜度也還是O(n^3),但是人家弗洛伊德演算法簡單啊,不僅思路簡單,程式碼還簡單。。。。正因為弗洛伊德演算法思想及其簡單,相當於暴力一遍,所以有個很大的限制就是頂點個數n被限制到200以內。

弗洛伊德演算法的思想:如果存在一個頂點k,使得以k作為中介點時可以使頂點 i 到頂點 j 的距離縮短,則使用 k 作為中介點,即 if(d[i][k]+d[k][j]<d[i][j]) d[i][j]=d[i][k]+d[k][j];

弗洛伊德演算法的具體實現

#define maxv 50
#define inf 100000000
int d[maxv][maxv]//d 除了i->i自身的距離初始化為0,其餘全部初始化為inf,表示不可達

void Floyd()
{
    for(int k=0;k<n;k++)//列舉頂點k
    {
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)//以k作為中介點,看是否能優化頂點 i 到 j 的距離
            {
                if(d[i][k]!=inf&&d[k][j]!=inf&&d[i][k]+d[k][j]<d[i][j])
                    d[i][j]=d[i][k]+d[k][j];
            }
        }
    }
}

AC程式碼:

#include <iostream>
#define maxv 51
#define inf 100000000
using namespace std;
int d[maxv][maxv];
int n,tem;

void Floyd()//3個for迴圈是弗洛伊德演算法的核心部分,這個演算法的思想非常簡單,相當於暴力了一遍
{
    for(int k=0;k<n;k++)
    {
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(d[i][k]!=inf&&d[k][j]!=inf&&d[i][k]+d[k][j]<d[i][j])
                    d[i][j]=d[i][k]+d[k][j];
            }
        }
    }
}

int main()
{
    fill(d[0],d[0]+maxv*maxv,inf);

    cin>>n;
    for(int i=0;i<n;i++)
        d[i][i]=0;

    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            cin>>tem;
            if(tem==0) continue;
            else
            {
                d[i][j]=tem;
            }
        }
    }
    Floyd();

    for(int i=0;i<n;i++)//請注意此題的輸出格式要求,這可以算是一個坑吧
    {
        for(int j=0;j<n;j++)
        {
            if(i==j&&j==n-1) cout<<0<<endl;
            else if(i==j&&j!=n-1) cout<<0<<" ";
            else if(d[i][j]==inf&&j==n-1) cout<<-1<<endl;
            else if(d[i][j]==inf&&j!=n-1) cout<<-1<<" ";
            else if(d[i][j]!=inf&&j!=n-1) cout<<d[i][j]<<" ";
            else if(d[i][j]!=inf&&j==n-1) cout<<d[i][j]<<endl;
        }
    }
    return 0;
}