hdu 2255奔小康賺大錢 KM算法模板
題目鏈接:http://acm.hdu.edu.cn/showproblem.php?
pid=2255
一,KM算法:(借助這個題寫一下個人對km的理解與km模板)
KM算法主要是用來求解圖的最優匹配的。
1。帶權二分圖: 在二分圖中每一條邊(x。y)相應一個權值Wi這樣的二分圖叫帶權二分圖。
一個匹配的權值就是該匹配中全部邊的權值之和。
2,最優匹配:
權值最大的一個完美匹配。叫做最優匹配。
《km算法思想》
對於一個帶權全然二分圖:G(V。E),對於當中每一條邊(x。y)邊都相應一個權值W(x,y)。
因為是二分圖所以節點集合能夠分解成兩個集合(X。Y)。X={x1。x2。,,。。xn},Y={y1,y2。,。。yn}。
可行性標簽:我們將在全部頂點上定義一個實函數F,且對全部定點滿足:F(x)+ F(y)>=W(x,y),
則我們稱F為圖G的一個可行性標簽。
(這裏能夠理解成F是對頂點加權值F(x))
平庸標簽:對於每個圖都有存在這樣一個標簽 l:
稱該標簽為平庸標簽
相等子圖:對於每個標簽都相應原圖的一個子圖:G’=(V’,E’) ,當中V’=V。E’屬於E,
且E’中全部邊滿足:F(x) + F(y)=W(x,y) 。該子圖稱為可行性標簽的F的相等子圖。
定理:(KM最重要的定理)
對於一個可行性標簽L,其相等子圖G’,若存在完美匹配M。則該匹配必是原圖G的完美匹配。
由於G中與G’中的節點是全然一樣的,且對於該匹配的權值為:
所以對於最大的可行性標簽L,若它的相等子圖G‘含有完美匹配M,則M就是原圖的最優匹配。
註意:上面的平庸匹配就是全部匹配中最大的匹配。
匈牙利樹:
匈牙利樹。事實上是一顆DFS搜索樹。
可是在搜索是有條件限制。
(1)。該樹必需要從未匹配點開始搜索。即,該樹必須以未匹配點為根。
(2)。並且在搜索過程中僅僅能走叫錯路。
(3)。葉子節點必須是匹配點
註意:若有葉子節點為未匹配點則從樹根到該節點一定有一條增廣路。
事實上在匈牙利算法中,若我們從一個未匹配的點。利用DFS找增廣路。若到最後我們沒找到增廣路。
則我們DFS搜索過程的路線就是一顆DFS樹且是一顆匈牙利樹,且在匈牙利算法中樹我們能夠永久
性的地把匈牙利樹從圖中刪去,而不影響結果。即:這顆樹對樹以外的節點的匹配沒有影響。
在上圖中(2)不是匈牙利樹由於有一個葉子節點7,非常明顯有一條2到7的增廣路。
(3)是一顆匈牙利樹。
更新可行性標簽:
當我們在當前大的可行性標簽l下。無法在L相等子圖中找到完美匹配,
這時我們要更新可行性標簽,
事實上就是在原來的可行性標簽l下降低最小的一個值(key)得到還有一個可行性標簽L。
更新方程為:
《KM算法》
如果二分圖為:G=(V,E),(X。Y)= V。X={x1。x2。,,。,xn}。Y={y1,y2,,,,yn}。
1。初始化可行性標簽L為平庸標簽(最大標簽)。
2。在可行性標簽相等子圖G’=(V。E’)中用匈牙利算法(DFS)對X集合中節點一個個進行匹配。
若在匹配過程中,若有X集合中的點沒匹配成功。轉向(3)
(3)匹配失敗這時DFS索搜出的路徑必然是一顆匈牙利樹。
由匈牙利樹性質可知匈牙利樹中的節點與邊與匈牙利樹外部是相互獨立的,所以我們改變匈牙利
樹種節點的可行新標簽就可以。且不會對其他樹外有影響,於是我們利用上面標簽轉化公式改變
匈牙利中節點的可行性標簽,然後再轉向(2)。
Km 例題:
令:圖節點為:V1={x1,x2,x3,x4,x5} 。V2={y1。y2,y3。y4,y5}。邊矩陣為:
初始化可行性標簽(平庸標簽)
L(y1)= L(y2) = L(y3) = L(y4) = L(y5) = 0,
L(x1)=max(3,5,5,4,1)=5 , L(x2)=max(2,2,0,2,2) =2 ,L(x3)=max(2,4,4,1,0) =4
L(x4)=max(0,1,1,0,0) =1 。L(x4)=max(0,1,1,0,0) =1 。
L的相等子圖為:
L相等子圖的匹配(匹配到x4點失敗)
x4搜索出的匈牙利樹為:
則 S={x4,x1,x3}。T={y2,y3} 利用可行性標簽變換公式為L’:
可得到:key=1;改變:
L’(x1)=4。L’(x2)= 2, L’(x4)=0 ; L’(y2)=1。L’(y3)=1。
然後x4繼續匹配:能夠匹配完:
這上圖L’完美匹配也就是原圖最優匹配。
W(M)=12。
代碼:
#include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> using namespace std; const int Max=310; const int Inf=(1<<31)-1; int N; int graph[Max][Max];//圖 int match[Max];//行匹配 int l_x[Max];//x可行性標簽 int l_y[Max];//y可行性標簽 int usedx[Max];//標記數組 int usedy[Max]; bool Hungary(int x) {//匈牙利DFS usedx[x]=true; for(int i=0;i<N;i++) { if(!usedy[i] && l_x[x]+l_y[i]==graph[x][i]) { usedy[i]=true; if(match[i]==-1 || Hungary(match[i])) { match[i]=x; return true; } } } return false; } void KM() { memset(l_x,0,sizeof(l_x)); memset(l_y,0,sizeof(l_y)); for(int i=0; i<N; i++) {//初始化可信性標簽 for(int j=0; j<N; j++) { if(l_x[i]<graph[i][j]) l_x[i]=graph[i][j]; } } for(int i=0;i<N;i++) {//匹配V1所以點 while(1) { memset(usedx,false,sizeof(usedx)); memset(usedy,false,sizeof(usedy)); if(Hungary(i)) break;//匹配成功就跳出 else {//匹配失敗 匈牙利樹節點更新 int tem=Inf;//求最小的 key for(int j=0;j<N;j++) {//用到那公式 if(usedx[j]) { for(int k=0;k<N;k++) { if(!usedy[k] && l_x[j]+l_y[k]-graph[j][k]<tem) tem= l_x[j]+l_y[k]-graph[j][k]; } } } for(int j=0;j<N;j++) {//更新匈牙利樹可行性標簽 if(usedx[j]) l_x[j]-=tem; if(usedy[j]) l_y[j]+=tem; } } } } } int main() { while(scanf("%d",&N)!=EOF) { for(int i=0; i<N; i++) for(int j=0; j<N; j++) scanf("%d",&graph[i][j]); memset(match,-1,sizeof(match)); KM(); int total=0; for(int i=0;i<N;i++) { total+=l_x[i]; total+=l_y[i]; } cout<<total<<endl; } return 0; }
hdu 2255奔小康賺大錢 KM算法模板