图论:关于二分图的总结(转载)
二分圖是這樣一個圖,它的頂點可以分類兩個集合X和Y,所有的邊關(guān)聯(lián)在兩個頂點中,恰好一個屬于集合X,另一個屬于集合Y。
?
最大匹配:圖中包含邊數(shù)最多的匹配稱為圖的最大匹配。
完美匹配:如果所有點都在匹配邊上,稱這個最大匹配是完美匹配。
最小覆蓋:最小覆蓋要求用最少的點(X集合或Y集合的都行)讓每條邊都至少和其中一個點關(guān)聯(lián)。
?
最小路徑覆蓋:用盡量少的不相交簡單路徑覆蓋有向無環(huán)圖G的所有結(jié)點。解決此類問題可以建立一個二分圖模型。把所有頂點i拆成兩個:X結(jié)點集中的i和Y結(jié)點集中的i',如果有邊i->j,則在二分圖中引入邊i->j',設(shè)二分圖最大匹配為m,則結(jié)果就是n-m。
?
最大獨立集問題: 在N個點的圖G中選出m個點,使這m個點兩兩之間沒有邊.求m最大值.如果圖G滿足二分圖條件,則可以用二分圖匹配來做.最大獨立集點數(shù)?=?N?-?最大匹配數(shù)
最小點覆蓋=最大匹配數(shù)
額,現(xiàn)在還沒想到如何證明……
一、匈牙利算法
設(shè)G=(V,{R})是一個無向圖。如頂點集V可分割為兩個互不相交的子集,并且圖中每條邊依附的兩個頂點都分屬兩個不同的子集。則稱圖G為二分圖。
?? ?? ?? 給定一個二分圖G,在G的一個子圖M中,M的邊集{E}中的任意兩條邊都不依附于同一個頂點,則稱M是一個匹配。
?? ????? 選擇這樣的邊數(shù)最大的子集稱為圖的最大匹配問題(maximal?matching?problem)?
? ? ???? 如果一個匹配中,圖中的每個頂點都和圖中某條邊相關(guān)聯(lián),則稱此匹配為完全匹配,也稱作完備匹配。
最大匹配在實際中有廣泛的用處,求最大匹配的一種顯而易見的算法是:先找出全部匹配,然后保留匹配數(shù)最多的。但是這個算法的復(fù)雜度為邊數(shù)的指數(shù)級函數(shù)。因此,需要尋求一種更加高效的算法。
匈牙利算法是求解最大匹配的有效算法,該算法用到了增廣路的定義(也稱增廣軌或交錯軌):若P是圖G中一條連通兩個未匹配頂點的路徑,并且屬M的邊和不屬M的邊(即已匹配和待匹配的邊)在P上交替出現(xiàn),則稱P為相對于M的一條增廣路徑。
由增廣路徑的定義可以推出下述三個結(jié)論:
?? ????? 1.???P的路徑長度必定為奇數(shù),第一條邊和最后一條邊都不屬于M。
?? ????? 2.???P經(jīng)過取反操作(即非M中的邊變?yōu)?span style="line-height:21px;">M中的邊,原來M中的邊去掉)可以得到一個更大的匹配M’。
?? ????? 3.???M為G的最大匹配當(dāng)且僅當(dāng)不存在相對于M的增廣路徑。
從而可以得到求解最大匹配的匈牙利算法:
?? ????? (1)置M為空
?? ????? (2)找出一條增廣路徑P,通過取反操作獲得更大的匹配M’代替M
?? ????? (3)重復(fù)(2)操作直到找不出增廣路徑為止
1 #include <iostream>
2 #include <cstring>
3 #include <cstdio>
4 using namespace std;
5 const int maxn=110;
6 int n,m,a,b,ans,cnt,fir[maxn],nxt[maxn],to[maxn],mat[maxn];
7 bool vis[maxn];
8 void addedge(int a,int b){
9 nxt[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;
10 }
11 bool Solve(int node){
12 vis[node]=true;
13 for(int i=fir[node];i;i=nxt[i]){
14 if(mat[to[i]]&&(vis[mat[to[i]]]||!Solve(mat[to[i]])))continue;
15 mat[to[i]]=node;
16 return true;
17 }
18 return false;
19 }
20 int main(){
21 freopen("flyer.in","r",stdin);
22 freopen("flyer.out","w",stdout);
23 scanf("%d%d",&n,&m);
24 while(scanf("%d%d",&a,&b)!=EOF)
25 addedge(a,b);
26 for(int i=1;i<=n-m;i++){
27 memset(vis,0,sizeof(vis));
28 if(Solve(i))
29 ans++;
30 }
31 printf("%d\n",ans);
32 return 0;
33 } 題目是”飛行員匹配“。
?
二、KM算法:
二分圖最優(yōu)匹配:對于二分圖的每條邊都有一個權(quán)(非負),要求一種完備匹配方案,使得所有匹配邊的權(quán)和最大,記做最優(yōu)完備匹配。(特殊的,當(dāng)所有邊的權(quán)為1時,就是最大完備匹配問題)
解二分圖最優(yōu)匹配問題可用窮舉的方法,但窮舉的效率=n!,所以我們需要更加優(yōu)秀的算法。
先說一個定理:設(shè)M是一個帶權(quán)完全二分圖G的一個完備匹配,給每個頂點一個可行頂標(biāo)(第i個x頂點的可行標(biāo)用lx[i]表示,第j個y頂點的可行標(biāo)用ly[j]表示),如果對所有的邊(i,j)?in?G,都有lx[i]+ly[j]>=w[i,j]成立(w[i,j]表示邊的權(quán)),且對所有的邊(i,j)?in?M,都有lx[i]+ly[j]=w[i,j]成立,則M是圖G的一個最優(yōu)匹配。
Kuhn-Munkras算法(即KM算法)流程:
?? ????? (1)初始化可行頂標(biāo)的值
?? ????? (2)用匈牙利算法尋找完備匹配
?? ????? (3)若未找到完備匹配則修改可行頂標(biāo)的值
?? ????? (4)重復(fù)(2)(3)直到找到相等子圖的完備匹配為止
KM算法主要就是控制怎樣修改可行頂標(biāo)的策略使得最終可以達到一個完美匹配,首先任意設(shè)置可行頂標(biāo)(如每個X節(jié)點的可行頂標(biāo)設(shè)為它出發(fā)的所有弧的最大權(quán),Y節(jié)點的可行頂標(biāo)設(shè)為0),然后在相等子圖中尋找增廣路,找到增廣路就沿著增廣路增廣。而如果沒有找到增廣路呢,那么就考慮所有現(xiàn)在在匈牙利樹中的X節(jié)點(記為S集合),所有現(xiàn)在在匈牙利樹中的Y節(jié)點(記為T集合),考察所有一段在S集合,一段在not?T集合中的弧,取???????delta?=?min?{l(xi)+l(yj)-w(xi,yj)?,?|?xi?in?S,?yj???in?not?T}????。明顯的,當(dāng)我們把所有S集合中的l(xi)減少delta之后,一定會有至少一條屬于(S,?not?T)的邊進入相等子圖,進而可以繼續(xù)擴展匈牙利樹,為了保證原來屬于(S,T?)的邊不退出相等子圖,把所有在T集合中的點的可行頂標(biāo)增加delta。隨后匈牙利樹繼續(xù)擴展,如果新加入匈牙利樹的Y節(jié)點是未蓋點,那么找到增廣路,否則把該節(jié)點的對應(yīng)的X匹配點加入匈牙利樹繼續(xù)嘗試增廣。
復(fù)雜度分析:由于在不擴大匹配的情況下每次匈牙利樹做如上調(diào)整之后至少增加一個元素,因此最多執(zhí)行n次就可以找到一條增廣路,最多需要找n條增廣路,故最多執(zhí)行n^2次修改頂標(biāo)的操作,而每次修改頂標(biāo)需要掃描所有弧,這樣修改頂標(biāo)的復(fù)雜度就是O(n^2)的,總的復(fù)雜度是O(n^4)的。
?????對于not?T的每個元素yj,定義松弛變量slack(yj)?=min{l(xi)+l(yj)-w(xi,yj),?|?xi?in?S},很明顯每次的delta?=?min{slack(yj),?|?yj???in?not?T},每次增廣之后用O(n^2)的時間計算所有點的初始slack,由于生長匈牙利樹的時候每條弧的頂標(biāo)增量相同,因此修改每個slack需要常數(shù)時間(注意在修改頂標(biāo)后和把已蓋Y節(jié)點對應(yīng)的X節(jié)點加入匈牙利樹的時候是需要修改slack的)。這樣修改所有slack值時間是O(n)的,每次增廣后最多修改n次頂標(biāo),那么修改頂標(biāo)的總時間降為O(n^2),n次增廣的總時間復(fù)雜度降為O(n^3)。事實上這樣實現(xiàn)之后對于大部分的數(shù)據(jù)可以比O(n^4)的算法快一倍左右。
利用二分圖匹配的匈牙利算法和KM算法,我們可以求解大部分的關(guān)于二分圖的問題,它們提供了求解最大匹配和最優(yōu)匹配的有效算法,在具體編程時我們只要多注意優(yōu)化,我們就可以得出求解這類問題的有效方法,從而可以對這類實際問題進行有效合理的解決。
另一種說法:
KM算法(轉(zhuǎn))
??? ? ? KM算法是通過給每個頂點一個標(biāo)號(叫做頂標(biāo))來把求最大權(quán)匹配的問題轉(zhuǎn)化為求完備匹配的問題的。設(shè)頂點Xi的頂標(biāo)為A[i],頂點Yi的頂標(biāo)為B?[i],頂點Xi與Yj之間的邊權(quán)為w[i,j]。在算法執(zhí)行過程中的任一時刻,對于任一條邊(i,j),A[i]+B[j]>=w[i,j]始終?成立。KM算法的正確性基于以下定理:
? 若由二分圖中所有滿足A[i]+B[j]=w[i,j]的邊(i,j)構(gòu)成的子圖(稱做相等子圖)有完備匹配,那么這個完備匹配就是二分圖的最大權(quán)匹配。
這個定理是顯然的。因為對于二分圖的任意一個匹配,如果它包含于相等子圖,那么它的邊權(quán)和等于所有頂點的頂標(biāo)和;如果它有的邊不包含于相等子圖,那么它的邊權(quán)和小于所有頂點的頂標(biāo)和。所以相等子圖的完備匹配一定是二分圖的最大權(quán)匹配。
? 初始時為了使A[i]+B[j]>=w[i,j]恒成立,令A[i]為所有與頂點Xi關(guān)聯(lián)的邊的最大權(quán),B[j]=0。如果當(dāng)前的相等子圖沒有完備匹配,就按下面的方法修改頂標(biāo)以使擴大相等子圖,直到相等子圖具有完備匹配為止。
? 我們求當(dāng)前相等子圖的完備匹配失敗了,是因為對于某個X頂點,我們找不到一條從它出發(fā)的交錯路。這時我們獲得了一棵交錯樹,它的葉子結(jié)點全部是X頂點?,F(xiàn)在我們把交錯樹中X頂點的頂標(biāo)全都減小某個值d,Y頂點的頂標(biāo)全都增加同一個值d,那么我們會發(fā)現(xiàn):
?兩端都在交錯樹中的邊(i,j),A[i]+B[j]的值沒有變化。也就是說,它原來屬于相等子圖,現(xiàn)在仍屬于相等子圖。
?兩端都不在交錯樹中的邊(i,j),A[i]和B[j]都沒有變化。也就是說,它原來屬于(或不屬于)相等子圖,現(xiàn)在仍屬于(或不屬于)相等子圖。
?X端不在交錯樹中,Y端在交錯樹中的邊(i,j),它的A[i]+B[j]的值有所增大。它原來不屬于相等子圖,現(xiàn)在仍不屬于相等子圖。
?X端在交錯樹中,Y端不在交錯樹中的邊(i,j),它的A[i]+B[j]的值有所減小。也就說,它原來不屬于相等子圖,現(xiàn)在可能進入了相等子圖,因而使相等子圖得到了擴大。
? 現(xiàn)在的問題就是求d值了。為了使A[i]+B[j]>=w[i,j]始終成立,且至少有一條邊進入相等子圖,d應(yīng)該等于min{A[i]+B[j]-w[i,j]|Xi在交錯樹中,Yi不在交錯樹中}。
? 以上就是KM算法的基本思路。但是樸素的實現(xiàn)方法,時間復(fù)雜度為O(n4)——需要找O(n)次增廣路,每次增廣最多需要修改O(n)次頂?標(biāo),每次修改頂標(biāo)時由于要枚舉邊來求d值,復(fù)雜度為O(n2)。實際上KM算法的復(fù)雜度是可以做到O(n3)的。我們給每個Y頂點一個“松弛量”函數(shù)?slack,每次開始找增廣路時初始化為無窮大。在尋找增廣路的過程中,檢查邊(i,j)時,如果它不在相等子圖中,則讓slack[j]變成原值與A?[i]+B[j]-w[i,j]的較小值。這樣,在修改頂標(biāo)時,取所有不在交錯樹中的Y頂點的slack值中的最小值作為d值即可。但還要注意一點:修改?頂標(biāo)后,要把所有的slack值都減去d。原文:http://www.360doc.com/content/11/0718/14/3701281_134273282.shtml
?
轉(zhuǎn)載于:https://www.cnblogs.com/TenderRun/p/5321381.html
總結(jié)
以上是生活随笔為你收集整理的图论:关于二分图的总结(转载)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 构建之法阅读笔记02
- 下一篇: GDC2016 Epic Games【B