二分图最优匹配「KM算法」
KM算法用來求二分圖最大權(quán)完美匹配
一般對KM算法的描述,基本上可以概括成以下幾個步驟:
(1) 初始化可行標桿
(2) 用匈牙利算法尋找完備匹配
(3) 若未找到完備匹配則修改可行標桿
(4) 重復(fù)(2)(3)直到找到相等子圖的完備匹配
二分圖匹配里面我們找最大邊進行連邊!
但是遇到某個點被匹配了兩次怎么辦?
那就用匈牙利算法進行更改匹配!
這就是KM算法的思路了:盡量找最大的邊進行連邊,如果不能則換一條較大的。引用鏈接
添加標桿(頂標)是怎樣子呢?我們對左邊每個點Xi和右邊每個點Yi添加標桿Cx和Cy。
其中我們要滿足Cx+Cy>=w[x][y](w[x][y]即為點Xi、Yi之間的邊權(quán)值)
對于一開始的初始化,我們對于每個點分別進行如下操作
Cx=max(w[x][y]);
Cy=0;
然后,我們可以進行連邊,即采用匈牙利算法,只是在判斷兩點之間是否有連線的條件下,因為我們要將最大邊進行連線,所以原來判斷是否有邊的條件w[x][y]==0換成了
Cx+Cy==w[x][y]
此時,有一個新的名詞——相等子圖。
因為我們通過了巧妙的處理讓計算機自動連接邊權(quán)最大的邊,換句話說,其他邊計算機就不會連了,也就“不存在”這個圖中,但我們可以隨時加上這些“不存在”圖中的邊。此時這個圖可以認為是原圖的子圖,并且是等效。
這樣,計算機在枚舉右邊的點的時候,滿足以上條件,就能夠知道這條邊是我們要連的最大的邊,就能進行連邊了。
于是乎我們連了AD。
接下來就尷尬了,計算機接下來要連B點的BD,但是D點已經(jīng)和A點連了,怎么辦呢???
根據(jù)匈牙利算法,我們做的是將A點與其他點進行連線,但此時的子圖里“不存在”與A點相連的其他邊,怎么辦呢??
為此,我們就需要加上這些邊!
很明顯,我們添邊,自然要加上不在子圖中邊權(quán)最大的邊,也就是和子圖里這個邊權(quán)值差最小的邊。
于是,我們再一度引入了一變量d,d=min{Cx[i]+Cy[j]-w[i][j]}
其中,在這個題目里Cx[i]指的是A的標桿,Cy[j]是除D點(即已連點)以外的點的標桿。
隨后,對于原先存在于子圖的邊AD,我們將A的標桿Cx[i]減去d,D的標桿Cy[d]加上d。
這樣,這就保證了原先存在AD邊保留在了子圖中,并且把不在子圖的最大權(quán)值的與A點相連的邊AE添加到了子圖。
因為計算機判斷一條邊是否在該子圖的條件是其兩端的頂點的標桿滿足
Cx+Cy==w[x][y]
對于原先的邊,我們對左端點的標桿減去了d,對右端點的標桿加上了d,所以最終的結(jié)果還是不變,仍然是w[x][y]。
對于我們要添加的邊,我們對于左端點減去了d,即Cx[i]=Cx[i]-d;為方便表示我們把更改后的的Cx[i]視為Cz[i],即Cz[i]=Cx[i]-d;
對于右端點,我們并沒有對其進行操作。那這條我們要添加邊的兩端點的標號是否滿足Cz[i]+Cy[j]=w[i][j]?
因為Cz[i]=Cx[i]-d;d=Cx[i]+Cy[j]-w[i][j];
我們把d代入左式可得Cz[i]=Cx[i]-(****Cx[i]+Cy[j]-w[i][j]);
化簡得Cz[i]+Cy[j]=w[i][j]。
------滿足了要求!即添加了新的邊。--------
值得注意的是,這里我們只是對于一條邊操作,當我們添加了幾條邊,要進行如上操作時,要保證原先存在的邊不消失,那么我們就要先求出了d,然后
對于每個連邊的左端點(記作集合S)的每個點的標號減去了d之后,然后連邊的右端點(記作T)加上d,這樣就保證了原先的邊不消失啦~
實際上這就是一直在尋找著增廣路,通過不斷修改標桿進行添邊實現(xiàn)。
接下來就繼續(xù)著匈牙利算法,直到完全匹配完為止。
該算法的正確性就在于 它每次都選擇最大的邊進行連邊
至此,我們再回顧KM算法的步驟:
*1.用鄰接矩陣(或其他方法也行啦)來儲存圖。*
2.運用貪心算法初始化標桿。
*3.運用匈牙利算法找到完備匹配。*
*4.如果找不到,則通過修改標桿,增加一些邊。*
5.重復(fù)3,4的步驟,直到完全匹配時可結(jié)束。
引用鏈接
現(xiàn)在有N男N女,有些男生和女生之間互相有好感,我們將其好感程度定義為好感度,我們希望把他們兩兩配對,并且最后希望好感度和最大。
怎么選擇最優(yōu)的配對方法呢?
首先,每個女生會有一個期望值,就是與她有好感度的男生中最大的好感度。男生呢,期望值為0,就是……只要有一個妹子就可以啦,不挑~~
這樣,我們把每個人的期望值標出來。
接下來,開始配對。
配對方法:
我們從第一個女生開始,分別為每一個女生找對象。
每次都從第一個男生開始,選擇一個男生,使男女兩人的期望和要等于兩人之間的好感度。
注意:每一輪匹配,每個男生只會被嘗試匹配一次!
具體匹配過程:
為女1找對象=
(此時無人配對成功)
根據(jù) “男女兩人的期望和要等于兩人之間的好感度”的規(guī)則
女1-男1:4+0 != 3
女1-男3:4+0 == 4
所以女1選擇了男3
女1找對象成功
==為女1找對象成功
為女2找對象=
(此時女1—男3)
根據(jù)配對原則,女2選擇男3
男3有主女1,女1嘗試換人
我們嘗試讓女1去找別人
嘗試失敗
為女2找對象失敗!
==為女2找對象失敗
這一輪參與匹配的人有:女1,女2,男3。
怎么辦???很容易想到的,這兩個女生只能降低一下期望值了,降低多少呢?
任意一個參與匹配女生能換到任意一個這輪沒有被選擇過的男生所需要降低的最小值(就可以保證每個女的都可以匹配到一個男的)
比如:女1選擇男1,期望值要降低1。 女2選擇男1,期望值要降低1。 女2選擇男2,期望值要降低2。
于是,只要期望值降低1,就有妹子可能選擇其他人。所以妹子們的期望值要降低1點。
同時,剛才被搶的男生此時非常得意,因為有妹子來搶他,于是他的期望值提高了1點(就是同妹子們降低的期望值相同)。
于是期望值變成這樣(當然,不參與剛才匹配過程的人期望值不變)
==繼續(xù)為女2找對象=
(此時女1—男3)
女2選擇了男1
男1還沒有被配對
女2找對象成功!
==為女2找對象成功=
為女3找對象=
(此時女1—男3,女2-男1)
女3沒有可以配對的男生……
女3找對象失敗
==為女3找對象失敗
此輪只有女3參與匹配
此時應(yīng)該為女3降低期望值
降低期望值1的時候,女3-男3可以配對,所以女3降低期望值1
==繼續(xù)為女3找對象
(此時女1—男3, 女2-男1)
女3相中了男3
此時男3已經(jīng)有主女1,于是女1嘗試換人
女1選擇男1
而男1也已經(jīng)有主女2,女2嘗試換人
前面說過,每一輪匹配每個男生只被匹配一次
所以女2換人失敗
女3找對象再次失敗
==為女3找對象失敗
這一輪匹配相關(guān)人員:女1,女2,女3,男1,男3
此時,只要女2降低1點期望值,就能換到男2
(前面提過 只要任意一個女生能換到任意一個沒有被選擇過的男生所需要降低的最小值)
我們把相應(yīng)人員期望值改變一下
==還是為女3找對象
(此時女1—男3, 女2-男1)
女3選擇了男3
男3有主女1,女1嘗試換人
女1換到了男1
男1已經(jīng)有主女2,女2嘗試換人
女2換人男2
男2無主,匹配成功!!!
==為女3找對象成功=
匹配成功!!!撒花~~
到此匹配全部結(jié)束
此時
女1-男1,女2-男2,女3-男3
好感度和為最大:9
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
int love[310][310];//每個女生對男生的好感度(W兩點之間的權(quán)值)
int exboy[310];//男生的吸引值(右標桿)
int exgirl[310];//女生的期望值(左標桿)
int visboy[310];//標記是否在此次匹配中出現(xiàn)
int visgirl[310];//標記是否在此次匹配中出現(xiàn)
int match[310];//標記每個男生匹配到的女生編號,沒有匹配到則為-1
int slack[310];//記錄每個男生想被女生匹配到最小需要多少吸引值
int n;
int Hungarian(int girl)
{
visgirl[girl]=1;//標記這個女生已經(jīng)在匹配中出現(xiàn)
for(int boy=1; boy<=n; boy++)
{
if(visboy[boy])//男生已經(jīng)被匹配,就繼續(xù)下一個男生
{
continue;
}
int tep=exboy[boy]+exgirl[girl]-love[girl][boy];
if(tep==0)//男生的吸引值加上女生的期望值==女生對男生的好感度(左標桿+右標桿==左標桿節(jié)點到右標桿節(jié)點的權(quán)值)
{
visboy[boy]=1;
if(match[boy]==-1||Hungarian(match[boy]))
{
match[boy]=girl;//標記這個男生匹配到的女生編號
return true;
}
}
else//意味著男生的吸引值不夠,記錄每個男生需要匹配到女生的最小吸引值
{
slack[boy]=min(slack[boy],tep);
}
}
return false;
}
void KM( )
{
memset(match,-1,sizeof(match));
memset(exboy,0,sizeof(exboy));//開始每個男生都沒有女生與他匹配,吸引值為0
for(int i=1; i<=n; i++)
{
exgirl[i]=love[i][1];
for(int j=2; j<=n; j++)
{
exgirl[i]=max(exgirl[i],love[i][j]);//初始化每個女生的期望值都是最大的(初始化左標桿)
}
}
// cout<<"flag"<<endl;
// for(int i=1;i<=n;i++)
// {
// cout<<exgirl[i]<<" "<<exboy[i]<<endl;
// }
for(int i=1; i<=n; i++)//每一個女生開始進行匹配
{
memset(slack,INF,sizeof(slack));//尋找最小的吸引值,所以初始化為正無窮
while(1)
{
memset(visgirl,0,sizeof(visgirl));
memset(visboy,0,sizeof(visboy));
if(Hungarian(i))//找到匹配策略,跳出循環(huán)
{
break;
}
//沒有找到匹配策略,于是更新女生的期望值(左標桿),男生的吸引值(右標桿)
int d=INF;
for(int j=1; j<=n; j++)
{
if(!visboy[j])
{
d=min(d,slack[j]);//找出最小的吸引值(那個男生就可以匹配到女生啦)
}
}
for(int j=1; j<=n; j++)
{
if(visgirl[j])//在匹配中出現(xiàn)的女生為了妥協(xié),期望值下降d(心里不甘心下降,于是盡可能的下降少一點期望值)
{
exgirl[j]-=d;
}
if(visboy[j])//在匹配中出現(xiàn)的男生由于有女生搶他,所以吸引值上升d(上升的值完全取決于女生想下降多少,于是就上升了最小的d)
{
exboy[j]+=d;
}
else//沒在匹配中的男生,由于女生的期望值降低,所以他需要的吸引值也下降d(不用那么辛苦訓(xùn)練吸引值了)
{
slack[j]-=d;
}
}
}
}
int res=0;
for(int i=1; i<=n; i++)
{
res+=love[match[i]][i];//第i個男生匹配到的女生編號是match[i]
}
printf("%d
",res);
}
int main( )
{
while(~scanf("%d",&n))
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
scanf("%d",&love[i][j]);
}
}
KM( );
}
return 0;
}
總結(jié)
以上是生活随笔為你收集整理的二分图最优匹配「KM算法」的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cookie test
- 下一篇: windows休眠文件可不可以删除