1. 程式人生 > >Dijkstra演算法小結

Dijkstra演算法小結

Dijkstra演算法應該是圖論裡最基礎的了,它所能解決的問題就是最短路之類的,它的核心內容就是“鬆弛操作”,這在演算法導論這本書中有詳細的介紹。給你一幅圖,給定起點和終點,求起點到終點的最短距離,這就是最短路問題,不論圖是有向還是無向,都可以用Dijkstra來解決。

舉個例子,給你一幅圖,要你求七點到終點的距離,你會怎麼做?枚舉出所有路徑???這個理論可行,但是當這幅圖比較複雜時,應該會比較吃力,而且會超時。這是dijkstra就派上用場了,首先我們定義兩個陣列,一個二維的的d[i][j],代表從i點到j點的距離,也就是兩點的邊權。當兩點不是直接相連時,應該初始為一個較大的數inf=10000000,如果是的d[i][i]我們可以賦值為0。第二個陣列是f[i],它的定義是從起點到i點的最短距離,我們設終點為N,那麼的d[N]是不是就是起點到N點的最短距離。好了,現在大致思路有了,問題就在於如何去求d[i]呢??

首先我們是不是可以想到起點到起點的最短距離是0,假設起點的下標為1,也就是說d[1]=0、、、然後除起點以外的點d[i]設為一個較大的數inf,之後我們是不不是可以用起點來更新周圍與它直接相連的點,狀態轉移方程是min(f[j],f[i]+d[i][j]),這個轉換是什麼意思呢,就是說i與j是直接相連的點,f[i]是起點到i的最短距離,那麼f[j]是不是f[i]+d[i][j](邊權)與當前f[j]的最小值。而在開始我們定義的f[1]=0,f[j]=inf,(一個很大的數),那麼與1直接相連的點是不是一定會被更新,當我們更新完第一步是,那之後的是不是也能繼續唄更新。

現在問題就是說當起點更新完它周圍的點時,起點f[1]是不能再被更新了,因為它在更新之前就是最小的了,它周圍的點勢必無法更新它,所以說,這個點是可以被刪除了,在之後的操作中它已經無效了。如何刪除能,我們可以在定義一個數組k[i],當然你也可以定義一個bool型陣列,true表示未被刪除,false表示已刪除,本人比較喜歡用1、0來表示。那麼現在我們應該找那個點來更新周圍的點呢??、、、、仔細考慮後是不是剩下的點中f[j]最小的點???為什麼呢???你想,它在當前已經是最小的了,周圍的點都比它大,加上兩點之間的邊權肯定比它大,也就是說它不可能再被更新了,但它可以用來更新周圍的點,更新完之後刪除,這樣直到更新完所有點。最短路也就求出來了。

附上程式碼

#include<stdio.h>
#include<string.h>
int d[112][112];//兩點間的距離,本題中此陣列可以不初始化。
int main()
{
    int N,M;
    while(scanf("%d%d",&N,&M)!=EOF&&(N!=0||M!=0))
    {
        int i,j,f[112],k[112],t;
        memset(d,0,sizeof(d));
        memset(k,0,sizeof(k));
        for(i=1;i<=M;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            scanf("%d",&d[a][b]);// 讀入資料
            d[b][a]=d[a][b];//無向圖的構圖
        }
        f[1]=0;//起點
        for(i=2;i<=N;i++)
            f[i]=9999999;//初始化
        int p=1;        
        
        while(1)
        {
            int judge=-1;
            for(t=1;t<=N;t++)
                if(k[t]==0)
                {
                    if(judge==-1||f[judge]>f[t])
                    judge=t;//尋找最小的那個點
                }
            
            if(judge==-1)
                break;
            for(j=1;j<=N;j++)
                if(d[judge][j]>0&&f[j]>f[judge]+d[judge][j])//更新周圍的點,也就是鬆弛操作
                    f[j]=f[judge]+d[judge][j];
                k[judge]=1;//刪除操作
    
        }
        printf("%d\n",f[N]);
        //for(i=1;i<=N;i++)
        //    printf("%d ",f[i]);
        
    }
    
    return 0;
}

/*
7 10
1 2 2
1 3 5
2 3 4
2 4 6
2 5 10
3 4 2
4 6 1
5 6 3
5 7 5
6 7 9    16
*/
接下來幾天如果有空也會陸續上傳一些最短路的題目及程式碼,與大家一起交流