使用 Warshall(沃舍爾)算法求解關係的傳遞閉包

1.離散數學定義:

t(R) = R u R^2 u R^3 u..... 其中R^(n+1) = R^n 複合 R

矩陣表示:

M(R) = M + M^2 + M^3 +....+M^n(其中加爲邏輯加)

所以我們只要按照這個公式每次更新M,最後的Mn就是傳遞閉包

 

2.Warshall算法:

(1)置新矩陣A=M; 

(2)i=1; 

(3)對所有j如果A[j,i]=1,則對k=1,2,…,n,A[j,k]=A[j,k]∨A[i,k]; 
(4)i加1;(i是行,j是列) 

 

(5)如果i≤n,則轉到步驟3),否則停止。 

 

思想:不難理解,對於每個相通的j - > i,我們可以從這個相通關係出發,看看能不能通過這條相通的j - > i,更新一下j - >k。對所有的可通關係都更新一遍M,最後的結果就是傳遞閉包了!

代碼:

#include <iostream>
#include<cstring>
using namespace std;
const int maxn = 100;
int G[maxn][maxn];//離散數學定義法
int main()
{
    int T;
    cin>>T;
    while(T--){
        memset(G,0,sizeof(G));
        int n,m;
        cin>>n>>m;//n個點m條邊
        for(int i = 1;i<=m;i++){
            int a,b;
            cin>>a>>b;
            G[a][b] = 1;//建邊
        }
        for(int i =1;i<=n;i++){//外層枚舉到達點
            for(int j = 1;j<=n;j++){//內層枚舉出發點
                if(G[j][i]){//如果j - >i相通
                    for(int k = 1;k<=n;k++){//從這條通路出發,更新所有的傳遞關係
                        G[j][k] = G[j][k]|G[i][k];(若G[j][k] = 0,但G[j][k] = G[j][i] 複合 G[i][k])
                    }
                }
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=n;j++){
                cout<<G[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    return 0;
}


3.在動態規劃思想上實現沃舍爾算法

(1)這個算法類似於最短路的floyd算法,可以說floyd是在更新傳遞閉包的基礎上記錄生成傳遞閉包的最小代價,這個最小代價就是最短路,所以說,最短路和沃舍爾求傳遞閉包的思想是一樣的或者是相通的!神奇!

代碼:

#include <iostream>
#include<cstring>
using namespace std;
const int maxn = 100;
int G[maxn][maxn];
int main()
{
    int T;
    cin>>T;
    while(T--){
        memset(G,0,sizeof(G));
        int n,m;
        cin>>n>>m;
        for(int i = 1;i<=m;i++){
            int a,b;
            cin>>a>>b;
            G[a][b] = 1;
        }
        for(int k = 1;k<=n;k++){//經過節點k中轉,能更新多少傳遞關係
            for(int i = 1;i<=n;i++){
                for(int j = 1;j<=n;j++){
                    if(G[i][j])continue;
                    G[i][j] = (G[i][k]&&G[k][j]);
                }
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=n;j++){
                cout<<G[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    return 0;
}


4.POj 3660 Cow Contest

(1)題意:有n頭牛互相比賽,現在給出m種已知的比賽結果。注:若A打敗B,B打敗C,則A可以打敗C。問你根據這個表最終能確定幾頭牛的排名。

(2)分析:能確定排名的肯定是和其他所有的牛的關係間接或者直接的知道了,所以這裏等價於求關係的傳遞閉包,某頭牛的所有入度和出度之和等於n - 1時表示這頭牛和其他所有牛的關係都有了那麼這頭牛的排名肯定就確定了。

(3)代碼:

​#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100+5;
int G[maxn][maxn];
int main()
{
    int n,m;
    memset(G,0,sizeof(G));
    scanf("%d%d",&n,&m);
    for(int i = 0;i<m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        G[a][b] = 1;
    }
    for(int k = 1;k<=n;k++){//求傳遞閉包
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=n;j++){
                if(i==j||G[i][j])continue;
                else if(G[i][k] == 1&&G[k][j]==1)G[i][j]  = 1;
            }
        }
    }
    int ans = 0;//統計總的可確定排名牛的數目
    for(int i = 1;i<=n;i++){
        int sum = 0;//統計編號i牛的度數和
        for(int j = 1;j<=n;j++){
            if(i==j)contiue;
            if(G[i][j]||G[j][i])sum++;
        }
        if(sum==n-1)ans++;
    }
    printf("%d\n",ans);
    return 0;
}

 

————————————————
原文鏈接:https://blog.csdn.net/qq_40772692/article/details/80351390

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章