KM最容易看懂的教程和參考程序

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

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

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

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

接下來,開始配對。

配對方法:

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

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

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

 

具體匹配過程:

==============爲女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點(就是同妹子們降低的期望值相同)。

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

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

(此時女1—男3)

女2選擇了男1

男1還沒有被配對

女2找對象成功!

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

 

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

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

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

女3找對象失敗

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

此輪只有女3參與匹配

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

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

==============繼續爲女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

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

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

==============還是爲女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

 

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

#include <iostream>
#include <ctime>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iomanip>
#include <stack>
using namespace std;
int w[1005][1005];
const int inf=(1<<20)-1;
int m,n;//n左m右 
int cx[1005],cy[1005];//頂標 
bool usex[1005],usey[1005];//本回合使用的x,y 
int link[1005];//link[i]=x代表:在y圖中的i與x相連 
bool dfs(int u){
    usex[u]=1;
    for(int i=1;i<=m;i++)
        if(!usey[i]&&cx[u]+cy[i]==w[u][i]){
            usey[i]=1;
            if(link[i]==-1||dfs(link[i])){//注意這裏要搜link[i]而不是i,因爲我們只搜索x側的,不需要搜索y側的 
                link[i]=u;
                return 1;   
            }
        }
return 0;
}
int KM(){
    memset(cy,0,sizeof(cy));
    memset(cx,-1,sizeof(cx));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cx[i]=max(cx[i],w[i][j]);
    for(int i=1;i<=n;i++){      
        while(1){
            int d=inf;
            memset(usex,0,sizeof(usex));
            memset(usey,0,sizeof(usey));
            if(dfs(i))break;
            for(int i=1;i<=n;i++)
                if(usex[i])
                    for(int j=1;j<=m;j++)
                        if(!usey[j])d=min(d,cx[i]+cy[j]-w[i][j]);
            if(d==inf)return -1;
            for(int i=1;i<=n;i++)
                if(usex[i])cx[i]-=d;
            for(int i=1;i<=m;i++)
                if(usey[i])cy[i]+=d;
        }
    }
    int ans=0;
    for(int i=1;i<=m;i++){
        if(~link[i])ans+=w[link[i]][i];
    }
    return ans; 
}
int main (){
    while(~scanf("%d%d",&n,&m)){
        memset(w,0,sizeof(w));
        memset(link,-1,sizeof(link));
        int a,b,c;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&w[i][j]);               
        printf("%d\n",KM());
    }   
    return 0;
}


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