1. 程式人生 > >圖論演算法----二分圖匹配----匈牙利演算法詳解

圖論演算法----二分圖匹配----匈牙利演算法詳解

一、一些相關概念

1、二分圖的概念:

二分圖又稱作二部圖,是圖論中的一種特殊模型。

設G=(V,E)是一個無向圖。 如果頂點集V可分割為兩個互不相交的子集X和Y,並且圖中每條邊連線的兩個頂點一個在X中,另一個在Y中,則稱圖G為二分圖。 2、判斷二分圖的方法: 一個圖是連通的,可以用如下的方法判定是否是二分圖: (1)在圖中任選一頂點v,定義其距離標號為0,然後把它的鄰接點的距離標號均設為1,接著把所有標號為1的鄰接點均標號為2(如果該點未標號的話),以此類推。 (2)標號過程可以用一次BFS實現。標號後,所有標號為奇數的點歸為X部,標號為偶數的點歸為Y部。 (3)接下來,二分圖的判定就是依次檢查每條邊,看兩個端點是否是一個在X部,一個在Y部。
如果一個圖不連通,則在每個連通塊中作判定。 3、二分圖匹配的概念: 給定一個二分圖G,在G的一個子圖M中,M的邊集{E}中的任意兩條邊都不依附於同一個頂點,則稱M是一個匹配。
圖中紅色的邊是數量為2的匹配。

4、最大匹配的概念: 選擇邊數最大的子圖M稱為圖的最大匹配問題 若二分圖X部的每一個頂點都與Y中的一個頂點匹配,並且Y部中的每一個頂點也與X部中的一個頂點匹配,則該匹配為完美匹配。 如果一個二分圖,X部中的每一個頂點都與Y部中的一個頂點匹配,或者Y部中的每一個頂點也與X部中的一個頂點匹配,則該匹配為完備匹配。 圖中所示為一個最大匹配,也是完備匹配,但不是完美匹配。

5、增廣路徑的概念:
設M為二分圖G已匹配邊的集合,若P是圖G中一條連通兩個未匹配頂點的路徑(P的起點在X部,終點在Y部,反之亦可),並且屬M的邊和不屬M的邊(即已匹配和待匹配的邊)在P上交替出現,則稱P為相對於M的一條增廣路徑。 增廣路徑是一條“交錯軌”。也就是說,它的第一條邊是目前還沒有參與匹配的,第二條邊參與了匹配,第三條邊沒有參與匹配......最後一條邊沒有參與匹配,並且起點和終點還沒有被選擇過

如圖,增廣路徑為C->I->E->F->A->H。 那麼,增廣路徑有什麼用呢?我們發現,把增廣路徑的所有邊取反(即匹配的變成未匹配,未匹配的邊成匹配), 則子圖M的邊數會加1,當找不到增廣路徑時,這就是最大匹配了。

6、增廣路徑的性質:

(1)增廣路徑的長度必定為奇數,第一條邊和最後一條邊都不屬於M,因為兩個端點分屬兩個集合,且未匹配。

(2)把一條增廣路徑經過取反操作可以得到一個更大的匹配M’。

(3)M為G的最大匹配當且僅當不存在相對於M的增廣路徑。

7、匈牙利演算法:

演算法輪廓:

(1)置M為空

(2)找出一條增廣路徑P,通過取反操作獲得更大的匹配M’代替M

(3)重複(2)操作直到找不出增廣路徑為止

我們採用DFS的辦法找一條增廣路徑: 從X部一個未匹配的頂點u開始,找一個未訪問的鄰接點v(v一定是Y部頂點)。對於v,分兩種情況:

(1)如果v未匹配,則已經找到一條增廣路

(2)如果v已經匹配,則取出v的匹配頂點w(w一定是X部頂點),邊(w,v)目前是匹配的,根據“取反”的想法,要將(w,v)改為未匹配,(u,v)設為匹配,能實現這一點的條件是看從w為起點能否找到一條增廣路徑P’。如果行,則u-v-P’就是一條以u為起點的增廣路徑。

程式碼:(有許多寫法)
int w[202][202],cx[202],cy[202],nx,ny;
bool visit[202];
bool dfs(int u)
{
    for(int i=1;i<=w[u][0];i++){
        int v=w[u][i];
        if(visit[v]==0){
            visit[v]=1;
            if(cy[v]==-1||dfs(cy[v])){
                cx[u]=v;cy[v]=u;
                return true;
            }
        }
    }
    return false;
}
int maxmatch()
{
    int ans=0;
    memset(cx,-1,sizeof(cx));
    memset(cy,-1,sizeof(cy));
    for(int i=0;i<=nx;i++){
        if(cx[i]==-1){ 
            memset(visit,0,sizeof(visit));
            ans+=dfs(i); 
        }
    }
    return ans;
}
8、匈牙利演算法的時空分析:

時間複雜度:

找一次增廣路徑的時間為:

鄰接矩陣: O(n^2)

鄰接表:O(n+m)

總時間:

鄰接矩陣:O(n^3)

       鄰接表:O(n*m)

空間複雜度:

  鄰接矩陣:O(n^2)

  鄰接表:   O(m+n)


二、幾個有關結論 1、最少點覆蓋集的點數就是最大匹配數M。 2、最少邊覆蓋集的邊數=總邊數-最大匹配數 3、最大獨立集點數=總點數-最大匹配數