1. 程式人生 > >Warshall傳遞閉包演算法的學習與實現

Warshall傳遞閉包演算法的學習與實現

1、問題引入

  一個有n個頂點的有向圖的傳遞閉包為:有向圖中的初始路徑可達情況可以參見其鄰接矩陣A,鄰接矩陣中A[i,j]表示i到j是否直接可達,若直接可達,則A[i,j]記為1,否則記為0;兩個有向圖中i到j有路徑表示從i點開始經過其他點(或者不經過其他點)能夠到達j點,如果i到j有路徑,則將T[i,j]設定為1,否則設定為0;有向圖的傳遞閉包表示從鄰接矩陣A出發,求的所有節點間的路徑可達情況,該矩陣就為所要求的傳遞閉包矩陣。。。

例如:

有向圖為:

由該有向圖可以得到初始的鄰接矩陣為:

那麼warshall傳遞閉包演算法的目的就是由鄰接矩陣出發,進行探索求出最終的傳遞閉包:

2、動態規劃求解思路

  動態規劃將問題分段,本例warshall演算法是通過一系列n階矩陣r(k)來構造最終階段n階傳遞閉包矩陣r(n)

      R(k) 由它的前趨 R(k-1) 計算得到(分級推進計算)。       R(0) ——該矩陣不允許它的路徑中包含任何中間頂點,即從該矩陣的任意頂點出發的路徑不含有中間頂點,此即鄰接矩陣。       R(1) ——允許路徑中包含第1個頂點(本例編號 1)作為中間頂點。       R(2) ——允許路徑中包含前2個頂點(本例編號1 2)作為中間頂點。       R(k) ——允許路徑中包含前k個頂點作為中間頂點。       R(n) ——允許路徑中包含全部 n 個頂點作為中間頂點。       每個後繼矩陣 R(k) 對其前趨 R(k-1) 來說,在路徑上允許增加一個頂點, 因此有可能包含更多的1(增加前為1的在增加後依然為1)。 3、具體的演算法描述
warshall(A[1...n,1...n]
r(0)<-A;
for(k=1;k<=n;k++)
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            r(k)[i,j]=r(k-1)[i,j] or(r(k-1)[i,k] and r(k-1)[k,j]);
return r(n);

4、具體實現程式碼如下
  說明:(1)有向圖的頂點個數和初始鄰接矩陣儲存在2.txt中(具體如下圖),其中4表示有向圖中有4 個頂點,其他表示初始鄰接矩陣。

     (2)有向圖的頂點個數和初始鄰接矩陣個數可以隨意更改,,,,

     具體程式碼:

#include<stdio.h>
#include<stdlib.h>
void Warshall(int,int**);
void main()
{
    int i,j,num;
    FILE*p;
    p=fopen("2.txt","r");
    if(p==NULL)
    {
        printf("cannot open 2.txt");
        exit(-1);
    }
    fscanf(p,"%d",&num);
    int **r=(int**)malloc(sizeof(int*)*(num+1));
    for(i=0;i<num+1;i++)
        r[i]=(int*)malloc(sizeof(int)*(num+1));
    for(i=1;i<num+1;i++)
        for(j=1;j<num+1;j++)
            fscanf(p,"%d",&r[i][j]);
    printf("頂點個數為:%d\n",num);
    printf("鄰接矩陣為:\n");
    for(i=1;i<num+1;i++)
    {
        for(j=1;j<num+1;j++)
            printf(" %d  ",r[i][j]);
        printf("\n");
    }
    Warshall(num,r);
    printf("最終的傳遞閉包為\n");
    for(i=1;i<num+1;i++)
    {
        for(j=1;j<num+1;j++)
            printf(" %d  ",r[i][j]);
        printf("\n");
    }

}
//三重迴圈實現的warshall演算法
//r為鄰接矩陣,中間儲存初試的可達與非可達路徑情況,1表示可達,0表示不可達
void Warshall(int num,int**r)
{
    int i,j,k;
    int **temp=(int**)malloc(sizeof(int*)*(num+1));
    for(i=0;i<num+1;i++)
        temp[i]=(int*)malloc(sizeof(int)*(num+1));
    for(k=1;k<=num;k++)//依次取得的可以作為中間點的頂點
    {
        for(i=1;i<=num;i++)
        {
            for(j=1;j<=num;j++)
            {
                temp[i][j]=(r[i][j])||(r[i][k]&r[k][j]);
            }
        }
        for(i=1;i<=num;i++)
            for(j=1;j<=num;j++)
                r[i][j]=temp[i][j];
    }

}

5、結果如下:

6、參考文獻

(1)演算法導論

(2)資料結構 嚴蔚敏