KM算法詳解+模板

原文地址: http://www.cnblogs.com/wenruo/p/5264235.html

KM算法用來求二分圖最大權完美匹配。

 

本文沒有給出KM算法的原理,只是模擬了一遍算法的過程。另,博主水平較差,發現問題歡迎指出,謝謝!!!!

 

現在有N男N女,有些男生和女生之間互相有好感,我們將其好感程度定義爲好感度,我們希望把他們兩兩配對,並且最後希望好感度和最大。

KM算法詳解-wenr

怎麼選擇最優的配對方法呢?

首先,每個女生會有一個期望值,就是與她有好感度的男生中最大的好感度。男生呢,期望值爲0,就是……只要有一個妹子就可以啦,不挑~~

這樣,我們把每個人的期望值標出來。

KM算法詳解-wenr

接下來,開始配對。

配對方法:

我們從第一個女生開始,分別爲每一個女生找對象。

每次都從第一個男生開始,選擇一個男生,使男女兩人的期望和要等於兩人之間的好感度

注意:每一輪匹配每個男生只會被嘗試匹配一次!

 

具體匹配過程:

==============爲女1找對象===============

(此時無人配對成功)

根據 “男女兩人的期望和要等於兩人之間的好感度”的規則

女1-男1:4+0 != 3

女1-男3:4+0 == 4

所以女1選擇了男3

女1找對象成功

==============爲女1找對象成功============

 

==============爲女2找對象===============

(此時女1—男3)

根據配對原則,女2選擇男3

男3有主女1,女1嘗試換人

我們嘗試讓女1去找別人

嘗試失敗

爲女2找對象失敗!

==============爲女2找對象失敗============

 

這一輪參與匹配的人有:女1,女2,男3。

怎麼辦???很容易想到的,這兩個女生只能降低一下期望值了,降低多少呢?

任意一個參與匹配女生能換到任意一個這輪沒有被選擇過的男生所需要降低的最小值

比如:女1選擇男1,期望值要降低1。 女2選擇男1,期望值要降低1。 女2選擇男2,期望值要降低2。

於是,只要期望值降低1,就有妹子可能選擇其他人。所以妹子們的期望值要降低1點。

同時,剛纔被搶的男生此時非常得意,因爲有妹子來搶他,於是他的期望值提高了1點(就是同妹子們降低的期望值相同)。

於是期望值變成這樣(當然,不參與剛纔匹配過程的人期望值不變)

KM詳解-wenr

==============繼續爲女2找對象=============

(此時女1—男3)

女2選擇了男1

男1還沒有被配對

女2找對象成功!

==============爲女2找對象成功=============

 

==============爲女3找對象===============

(此時女1—男3,女2-男1)

女3沒有可以配對的男生……

女3找對象失敗

==============爲女3找對象失敗============

此輪只有女3參與匹配

此時應該爲女3降低期望值

降低期望值1的時候,女3-男3可以配對,所以女3降低期望值1

KM算法詳解

 

==============繼續爲女3找對象============

(此時女1—男3, 女2-男1)

女3相中了男3

此時男3已經有主女1,於是女1嘗試換人

女1選擇男1

而男1也已經有主女2,女2嘗試換人

前面說過,每一輪匹配每個男生只被匹配一次

所以女2換人失敗

女3找對象再次失敗

==============爲女3找對象失敗============

這一輪匹配相關人員:女1,女2,女3,男1,男3

此時,只要女2降低1點期望值,就能換到男2

(前面提過 只要任意一個女生能換到任意一個沒有被選擇過的男生所需要降低的最小值)

我們把相應人員期望值改變一下

KM算法詳解-wenr

 

==============還是爲女3找對象============

(此時女1—男3, 女2-男1)

女3選擇了男3

男3有主女1,女1嘗試換人

女1換到了男1

男1已經有主女2,女2嘗試換人

女2換人男2

男2無主,匹配成功!!!

==============爲女3找對象成功=============

匹配成功!!!撒花~~

到此匹配全部結束

此時

女1-男1,女2-男2,女3-男3

好感度和爲最大:9

 

 

雖然不停換人的過程聽起來很麻煩,但其實整個是個遞歸的過程,實現起來比較簡單。比較複雜的部分就是期望值的改變,但是可以在遞歸匹配的過程中順帶求出來。

 

模板(帶詳細註釋)(入門題:HDU2255(複雜度應該是O(N^3)

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
const int N=310;
int n;
int e[N][N]; //關係
int ex_girl[N];//女生的期望值
int ex_boy[N];//男生的期望值
int match[N]; //記錄每個男生匹配到的女生,如果沒有爲-1
bool girl[N];//標記每一輪匹配過的女生
bool boy[N];//標記每一輪匹配過的男生
int slack[N];//記錄每個男生要被女生選中還需多少期望值
bool line(int x)
{
    girl[x]=1;
    for(int i=1;i<=n;i++){
        if(boy[i])
            continue;
        int k=ex_boy[i]+ex_girl[x]-e[x][i];
        if(k==0){ //符合要求
            boy[i]=1;
            if(match[i]==-1||line(match[i])){
                match[i]=x;
                return true;
            }
        }
        else slack[i]=min(slack[i],k);
    }
    return false;
}
int km()
{
    met(ex_boy,0);
    met(match,-1);
    for(int i=1;i<=n;i++){
        ex_girl[i]=e[i][1];
        for(int j=2;j<=n;j++)
            ex_girl[i]=max(ex_girl[i],e[i][j]);  //初始每個女生的期望值爲對每個男生的期望的最大值
    }
    for(int i=1;i<=n;i++){  //爲每一個女生匹配
        met(slack,inf);
        while(1){
            met(girl,0);
            met(boy,0);
            if(line(i))  //匹配成功,退出
                break;
            int d=inf; //匹配失敗
            for(int j=1;j<=n;j++)
                if(!boy[j])
                d=min(d,slack[j]);  //最小可降低的期望值
            for(int j=1;j<=n;j++){
                if(girl[j])
                    ex_girl[j]-=d;  //所以找到過的女生降低期望值
                if(boy[j])
                    ex_boy[j]+=d; //所有找到過的男生增加期望值
                else slack[j]-=d; //沒有訪問過的boy 因爲girl們的期望值降低,距離得到女生傾心又進了一步!
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)
        ans+=e[match[i]][i];
    return ans;
}
int main()
{
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            scanf("%d",&e[i][j]);
        printf("%d\n",km());
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章