KM 最优匹配 讲解
轉:
基本原理
該算法是通過給每個頂點一個標號(叫做頂標)來把求最大權匹配的問題轉化為求完備匹配的問題的。設頂點Xi的頂標為A[ i ],頂點Yj的頂標為B[ j ],頂點Xi與Yj之間的邊權為w[i,j]。在算法執行過程中的任一時刻,對于任一條邊(i,j),A[ i ]+B[j]>=w[i,j]始終成立。
?
KM算法的正確性基于以下定理:
?
若由二分圖中所有滿足A[ i ]+B[j]=w[i,j]的邊(i,j)構成的子圖(稱做相等子圖)有完備匹配,那么這個完備匹配就是二分圖的最大權匹配。
?
這個定理是顯然的。因為對于二分圖的任意一個匹配,如果它包含于相等子圖,那么它的邊權和等于所有頂點的頂標和;如果它有的邊不包含于相等子圖,那么它的邊權和小于所有頂點的頂標和。所以相等子圖的完備匹配一定是二分圖的最大權匹配。
?
初始時為了使A[ i ]+B[j]>=w[i,j]恒成立,令A[ i ]為所有與頂點Xi關聯的邊的最大權,B[j]=0。如果當前的相等子圖沒有完備匹配,就按下面的方法修改頂標以使擴大相等子圖,直到相等子圖具有完備匹配為止。
?
我們求當前相等子圖的完備匹配失敗了,是因為對于某個X頂點,我們找不到一條從它出發的交錯路。這時我們獲得了一棵交錯樹,它的葉子結點全部是X頂點。現在我們把交錯樹中X頂點的頂標全都減小某個值d,Y頂點的頂標全都增加同一個值d,那么我們會發現:
?
1)兩端都在交錯樹中的邊(i,j),A[ i ]+B[j]的值沒有變化。也就是說,它原來屬于相等子圖,現在仍屬于相等子圖。
?
2)兩端都不在交錯樹中的邊(i,j),A[ i ]和B[j]都沒有變化。也就是說,它原來屬于(或不屬于)相等子圖,現在仍屬于(或不屬于)相等子圖。
?
3)X端不在交錯樹中,Y端在交錯樹中的邊(i,j),它的A[ i ]+B[j]的值有所增大。它原來不屬于相等子圖,現在仍不屬于相等子圖。
?
4)X端在交錯樹中,Y端不在交錯樹中的邊(i,j),它的A[ i ]+B[j]的值有所減小。也就說,它原來不屬于相等子圖,現在可能進入了相等子圖,因而使相等子圖得到了擴大。
?
現在的問題就是求d值了。為了使A[ i ]+B[j]>=w[i,j]始終成立,且至少有一條邊進入相等子圖,d應該等于:
?
Min{A[ i ]+B[j]-w[i,j] | Xi在交錯樹中,Yi不在交錯樹中}。
?
改進
以上就是KM算法的基本思路。但是樸素的實現方法,時間復雜度為O(n4)——需要找O(n)次增廣路,每次增廣最多需要修改O(n)次頂標,每次修改頂標時由于要枚舉邊來求d值,復雜度為O(n2)。實際上KM算法的復雜度是可以做到O(n3)的。我們給每個Y頂點一個“松弛量”函數slack,每次開始找增廣路時初始化為無窮大。在尋找增廣路的過程中,檢查邊(i,j)時,如果它不在相等子圖中,則讓slack[j]變成原值與A[ i ]+B[j]-w[i,j]的較小值。這樣,在修改頂標時,取所有不在交錯樹中的Y頂點的slack值中的最小值作為d值即可。但還要注意一點:修改頂標后,要把所有的不在交錯樹中的Y頂點的slack值都減去d。
?
?
Kuhn-Munkras算法流程:
?
(1)初始化可行頂標的值
?
(2)用匈牙利算法尋找完備匹配
?
(3)若未找到完備匹配則修改可行頂標的值
?
(4)重復(2)(3)直到找到相等子圖的完備匹配為止
1 bool find(int x) 2 {//匈牙利算法尋找x的增廣路徑 3 //以x為根的M的交錯樹 4 //看來本算法需要二部圖兩部分的頂點個數都相等吧 5 int y, t; 6 visitx[x] = true; 7 for( y = 0; y < N; y++ ) 8 { 9 if( visity[y] ) continue;//找增廣路徑的過程中不妨問已經訪問過的頂點 10 t = lx[x] + ly[y] - w[x][y];//是在等子圖中尋找匹配的增廣路徑 11 if( t == 0 ) 12 { 13 visity[y] = true; 14 if( linky[y] == -1 || find(linky[y]) ) 15 { 16 linky[y] = x; 17 return true; 18 } 19 } 20 else 21 {//因為本來就需要將一條x頂點在交錯樹中,y頂點不在交錯樹中的邊擴展進交錯樹來 22 //所以只改變這些不在等子圖中的邊的y頂點的松弛量 23 if( slack[y] > t ) 24 slack[y]=t; 25 } 26 } 27 return false; 28 } 29 //外層的匈牙利算法需要O(2)的時間,而修改頂標時由于要枚舉所有的邊所以也需要O(2)的時間 30 //所以總時間是O(4) 31 //引入松弛量以后改變頂標就不需要枚舉每一條邊,只需要枚舉不在交錯樹中的y的松弛量,所以 32 //時間復雜度降為O(3) 33 void KM() 34 {//KM算法尋找圖的最大權匹配 35 int i, j, x, d; 36 memset(linky,-1,sizeof(linky)); 37 memset(lx,0,sizeof(lx)); //x的頂標 38 memset(ly,0,sizeof(ly));//y的頂標 39 for( i = 0; i < N; i++) 40 for( j = 0; j < N; j++ ) 41 if( map[i][j] > lx[i] ) 42 lx[i] = map[i][j];//一開始x的頂標為所有與x相連的邊中權值最大的邊的權值,y的頂標為0 43 for( x = 0; x < N; x++ ) 44 {//在匈牙利算法中從每個x出發尋找增廣路,如果找到就在匹配值上加1,這是為了尋找最大匹配 45 //而在此處,必須找到完備匹配,所以對于每一個x中的頂點,找到其增廣路就跳出,找不到的話 46 //就需要修改頂標值直至找到為止 47 for( i = 0; i < N; i++ ) 48 slack[i] = INF;//松弛變量 49 while (true) 50 {//無限循環直至找到完備匹配 51 memset(visitx, 0, sizeof(visitx));//visx為真表示的是該頂點是匹配中的頂點 52 memset(visity, 0, sizeof(visity));//y同理 53 if( find(x) ) break; 54 d = INF; 55 for( i = 0; i < N; i++) 56 { 57 if ( !visity[i] )//注意是取所有不在交錯樹中的y頂點的松弛量的最小值作為d的值 58 if ( d > slack[i] ) 59 d = slack[i]; 60 } 61 for( i = 0; i < N; i++ ) 62 { 63 if( visitx[i] ) 64 lx[i] -= d; 65 } 66 for( i = 0; i < N; i++ ) 67 { 68 if( visity[i] ) 69 ly[i] += d; 70 else 71 slack[i] -= d; 72 } 73 } 74 } 75 }轉載于:https://www.cnblogs.com/shenshuyang/archive/2012/08/07/2626102.html
總結
以上是生活随笔為你收集整理的KM 最优匹配 讲解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: silverlight 如何在浏览器的新
- 下一篇: About IndexDB(转)