1. 程式人生 > >ACM-最短路(SPFA,Dijkstra,Floyd)之最短路——hdu2544

ACM-最短路(SPFA,Dijkstra,Floyd)之最短路——hdu2544

***************************************轉載請註明出處:http://blog.csdn.net/lttree***************************************


最短路

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 28761    Accepted Submission(s): 12444


Problem Description 在每年的校賽裡,所有進入決賽的同學都會獲得一件很漂亮的t-shirt。但是每當我們的工作人員把上百件的衣服從商店運回到賽場的時候,卻是非常累的!所以現在他們想要尋找最短的從商店到賽場的路線,你可以幫助他們嗎?


Input 輸入包括多組資料。每組資料第一行是兩個整數N、M(N<=100,M<=10000),N表示成都的大街上有幾個路口,標號為1的路口是商店所在地,標號為N的路口是賽場所在地,M則表示在成都有幾條路。N=M=0表示輸入結束。接下來M行,每行包括3個整數A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A與路口B之間有一條路,我們的工作人員需要C分鐘的時間走過這條路。
輸入保證至少存在1條商店到賽場的路線。

Output 對於每組輸入,輸出一行,表示工作人員從商店走到賽場的最短時間
Sample Input 2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0
Sample Output 3 2

求最短路,我是看《挑戰程式設計競賽》裡的書學的。

裡面介紹了三種方法: Bellman-Ford、Dijkstra and Floyd

三者區別也都很明顯:

Bellman-Ford:

求單源最短路,可以判斷有無負權迴路(若有,則不存在最短路), 時效性較好,時間複雜度O(VE)。

Bellman-Ford演算法是求解單源最短路徑問題的一種演算法。

      單源點的最短路徑問題是指: 給定一個加權有向圖G和源點s,對於圖G中的任意一點v,求從s到v的最短路徑。

      與Dijkstra演算法不同的是,在Bellman-Ford演算法中,邊的權值可以為負數。       設想從我們可以從圖中找到一個環路(即從v出發,經過若干個點之後又回到v)且這個環路中所有邊的權值之和為負。那麼通過這個環路,環路中任意兩點的最短路徑就可以無窮小下去。如果不處理這個負環路,程式就會永遠執行下去。 而Bellman-Ford演算法具有分辨這種負環路的能力。


Dijkstra:

求單源、無負權的最短路。時效性較好,時間複雜度為O(V*V+E)。 源點可達的話,O(V*lgV+E*lgV)=>O(E*lgV)。       當是稀疏圖的情況時,此時E=V*V/lgV,所以演算法的時間複雜度可為O(V^2) 。若是斐波那契堆作優先佇列的話,演算法時間複雜度,則為O(V*lgV + E)。 

Floyd:

求多源、無負權邊的最短路。用矩陣記錄圖。時效性較差,時間複雜度O(V^3)。        Floyd-Warshall演算法(Floyd-Warshall algorithm)是解決任意兩點間的最短路徑的一種演算法,可以正確處理有向圖或負權的最短路徑問題。

Floyd-Warshall演算法的時間複雜度為O(N^3),空間複雜度為O(N^2)。

      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)。

      在實際演算法中,為了節約空間,可以直接在原來空間上進行迭代,這樣空間可降至二維。 Floyd-Warshall演算法的描述如下: for k ← 1 to n do   for i ← 1 to n do     for j ← 1 to n do       if (Di,k + Dk,j < Di,j) then         Di,j ← Di,k + Dk,j; 其中Di,j表示由點i到點j的代價,當Di,j為 ∞ 表示兩點之間沒有任何連線。

後來,我看Bellman-Ford的佇列優化,SPFA(Shortest Path Faster Algorithm)。

SPFA:

是Bellman-Ford的佇列優化,時效性相對好,時間複雜度O(kE)。(k<<V)。

與Bellman-ford演算法類似,SPFA演算法採用一系列的鬆弛操作以得到從某一個節點出發到達圖中其它所有節點的最短路徑。所不同的是,SPFA演算法通過維護一個佇列,使得一個節點的當前最短路徑被更新之後沒有必要立刻去更新其他的節點,從而大大減少了重複的操作次數。

      SPFA演算法可以用於存在負數邊權的圖,這與dijkstra演算法是不同的。

與Dijkstra演算法與Bellman-ford演算法不同,SPFA的演算法時間效率是不穩定的,即它對於不同的圖所需要的時間有很大的差別。

      在最好情形下,每一個節點都只入隊一次,則演算法實際上變為廣度優先遍歷,其時間複雜度僅為O(E)。另一方面,存在這樣的例子,使得每一個節點都被入隊(V-1)次,此時演算法退化為Bellman-ford演算法,其時間複雜度為O(VE)。

      SPFA演算法在負邊權圖上可以完全取代Bellman-ford演算法,另外在稀疏圖中也表現良好。但是在非負邊權圖中,為了避免最壞情況的出現,通常使用效率更加穩定的Dijkstra演算法,以及它的使用堆優化的版本。通常的SPFA演算法在一類網格圖中的表現不盡如人意。

然後,這道題我用了SPFA,Dijkstra和Floyd來做(Bellman-Ford 太慢,就不做了)

這道題,當時我怎麼做,做不出來,後來發現是 MAX 設定為 0x7fffffff 問題。設定成別的大數就沒事。

貢獻了N個WA啊!!!

鬱悶ING。。。

SPFA:

/****************************************
*****************************************
*        Author:Tree                    *
*From :http://blog.csdn.net/lttree      *
* Title : 最短路                       *
*Source: hdu 2544                       *
* Hint : SPFA                           *
*****************************************
****************************************/

#include <stdio.h>
#include <queue>
using namespace std;

#define RANGE 101
#define MAX 0x3f3f3f3f
int cost[RANGE][RANGE];
int d[RANGE];
bool used[RANGE];
int n,m;

void spfa( int s )
{
    int i,now;
    // 初始化
    for( i=1;i<=n;++i )
    {
        d[i]=MAX;
        used[i]=false;
    }

    d[s]=0;
    queue <int> q;
    q.push(s);
    used[s] = true;

    while(!q.empty())
    {
        now = q.front();
        q.pop();
        used[now] = false;
        for(i = 1; i <= n; i++)
        {
            if(d[i] > d[now] + cost[now][i])
            {
                d[i] = d[now] + cost[now][i];
                if(used[i] == 0)
                {
                    q.push(i);
                    used[i] = true;
                }
            }
        }
    }
}

int main()
{
    int i,j,A,B,C;
    while( scanf("%d%d",&n,&m) )
    {
        if( !n && !m )  break;
        // 初始化
        for( i=1;i<=n;++i )
            for( j=1;j<=i;++j )
                if( i==j )  cost[i][j]=0;
                else    cost[i][j]=cost[j][i]=MAX;

        for( i=0;i<m;++i )
        {
            scanf("%d%d%d",&A,&B,&C);
            cost[A][B]=cost[B][A]=C;
        }

        spfa(1);
        printf("%d\n",d[n]);
    }
    return 0;
}

Dijkstra:
/****************************************
*****************************************
*        Author:Tree                    *
*From :http://blog.csdn.net/lttree      *
* Title : 最短路                       *
*Source: hdu 2544                       *
* Hint : Dijkstra                       *
*****************************************
****************************************/

#include <stdio.h>
#define MAX 0x3f3f3f3f
#define RANGE 101

int cost[RANGE][RANGE];
int d[RANGE];
bool used[RANGE];

int n,m;
int Min( int a,int b )
{
    return a<b?a:b;
}

void Dijkstra( int s )
{
    int i,v,u;
    for( i=1;i<=n;++i )
    {
        used[i]=false;
        d[i]=cost[1][i];
    }
    d[s]=0;

    while( true )
    {
        v=-1;
        for( u=1;u<=n;++u )
            if( !used[u] && ( v==-1 || d[u]<d[v]) )
                v=u;
        if( v==-1 ) break;
        used[v]=true;

        for( u=1;u<=n;++u )
            d[u]=Min( d[u],d[v]+cost[v][u] );
    }
}

int main()
{
    int A,B,C,i,j;

    while( scanf("%d%d",&n,&m) )
    {
        if( !n && !m )  break;

        // 初始化
        for( i=1;i<=n;++i )
            for( j=1;j<=i;++j )
                if( i==j )  cost[i][j]=0;
                else    cost[i][j]=cost[j][i]=MAX;

        for( i=0;i<m;++i )
        {
            scanf("%d%d%d",&A,&B,&C);
            cost[A][B]=cost[B][A]=C;
        }

        Dijkstra(1);
        printf("%d\n",d[n]);
    }
    return 0;
}

Floyd:
/****************************************
*****************************************
*        Author:Tree                    *
*From :http://blog.csdn.net/lttree      *
* Title : 最短路                       *
*Source: hdu 2544                       *
* Hint : Floyd                          *
*****************************************
****************************************/

#include <stdio.h>
#define MAX 0x3f3f3f3f
#define RANGE 105

int d[RANGE][RANGE];
int n;

int Min( int a,int b )
{
    return a<b?a:b;
}
void warshall_floyd( void )
{
    int i,j,k;
    for( k=1;k<=n;++k )
        for( i=1;i<=n;++i )
            for( j=1;j<=n;++j )
                d[i][j]=Min( d[i][j],d[i][k]+d[k][j] );
}

int main()
{
    int m,A,B,C,i,j;

    while( scanf("%d%d",&n,&m) )
    {
        if( !n && !m )  break;

        // 初始化
        for( i=1;i<=n;++i )
            for( j=1;j<=i;++j )
            {
                if( i==j ) d[i][j]=0;
                else    d[i][j]=d[j][i]=MAX;
            }

        // 輸入
        for( i=0;i<m;++i )
        {
            scanf("%d%d%d",&A,&B,&C);
            d[A][B]=d[B][A]=C;
        }

        // floyd演算法求最短路
        warshall_floyd();
        printf("%d\n",d[1][n]);
    }
    return 0;
}