1. 程式人生 > >HDU2063 二分圖最大匹配 增廣路演算法入門

HDU2063 二分圖最大匹配 增廣路演算法入門

增廣路定理:我們用未蓋點來表示不與任何匹配邊鄰接的點,其他點為匹配點,即恰好和一條匹配邊鄰接的點。從未蓋點出發,依次經過非匹配邊,匹配邊,非匹配邊,匹配邊……所得到的路稱為交替路。如果交替路的終點是一個未蓋點,則稱這條交替路為一條增廣路。增廣路中,非匹配邊比匹配邊多一條。

增廣路的作用是改進匹配,假設我們已經找到一個匹配,如何判斷他是否是最大匹配? 看增廣路,如果有一條增廣路,那麼把此路上的匹配邊和非匹配邊互換,得到的匹配比剛才的多一條。 反之,若找不到增廣路,則當前匹配就是最大匹配

一個匹配是最大匹配的充要條件是不存在增廣路,這個充要條件適用於任意圖。

增廣路演算法:根據增廣路定理,最大匹配可以通過反覆勳章增廣路來求解,如何找到增廣路呢?根據定義,首先需要選一個未蓋點u作為起點,設這個u是X結點。接下來需要選一個從u出發的非匹配邊(u,v) 到達Y結點v。如果v是未蓋點,說明我們成功找到了一條增廣路,如果v是匹配點,那我們下一步得走匹配邊,因為一個匹配點恰好與一個匹配邊鄰接。設匹配點v鄰接的匹配邊的另一端為left[v],那麼可以理解從u直接走到了left[v],而這個left[v]也是一個X結點。如果始終沒有找到未蓋點,最後會擴展出一顆匈牙利樹。

這樣,我們得到了一個演算法,即每次選一個未蓋點u進行DFS,注意,如果找不到以u開頭的增廣路,則換一個未蓋點進行DFS,且以後再也不從u出發找增廣路。換句話說,如果以後存在一個從u出發的增廣路,那麼現在就找得到。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=600+5;
bool line[maxn][maxn];
//line[x][y]=true表示x號女生喜歡y號男生(邊)
int boy[maxn];
//儲存y號男生匹配邊另一端的匹配點女生,如果是未蓋點則為0
bool use[maxn];
//儲存y號男生是否在這條交替路上被使用過
int k,n,m;
bool dfs(int x){
    for(int j=1;j<=m;j++){
            if(line[x][j]&&!use[j]){
                use[j]=true;
                if(!boy[j]||dfs(boy[j])){
            //滿足上面的判斷語句說明找到了增廣路
            //即終點是未蓋點的交替路
                    boy[j]=x;
                    return true;
                }
            }
        //該條交替路不是增廣路則需要找下一條交替路
    }
return false;
//無法找到增廣路則未蓋點x的dfs失敗,不存在以x為起點的增廣路
}
int main(){
   // freopen("datain.txt","r",stdin);
    while(cin>>k){
            if(!k) break;
        cin>>n>>m;//k為邊數,n為女生數,m為男生數
        int x,y;int ans=0;
        memset(line,false,sizeof(line));
        memset(boy,0,sizeof(boy));
        while(k--){
            cin>>x>>y;
            line[x][y]=true;
        }
        for(int i=1;i<=n;i++){
            memset(use,false,sizeof(use));
            if(dfs(i))ans++;//為一個女生dfs成功一次,則ans++
        }
        cout<<ans<<endl;
    }
}