日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 综合教程 >内容正文

综合教程

二分图最优匹配「KM算法」

發(fā)布時(shí)間:2023/12/19 综合教程 29 生活家
生活随笔 收集整理的這篇文章主要介紹了 二分图最优匹配「KM算法」 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

KM算法用來求二分圖最大權(quán)完美匹配
一般對(duì)KM算法的描述,基本上可以概括成以下幾個(gè)步驟:
(1) 初始化可行標(biāo)桿
(2) 用匈牙利算法尋找完備匹配
(3) 若未找到完備匹配則修改可行標(biāo)桿
(4) 重復(fù)(2)(3)直到找到相等子圖的完備匹配

二分圖匹配里面我們找最大邊進(jìn)行連邊!
但是遇到某個(gè)點(diǎn)被匹配了兩次怎么辦?
那就用匈牙利算法進(jìn)行更改匹配
這就是KM算法的思路了:盡量找最大的邊進(jìn)行連邊,如果不能則換一條較大的。

引用鏈接
添加標(biāo)桿(頂標(biāo))

是怎樣子呢?我們對(duì)左邊每個(gè)點(diǎn)Xi和右邊每個(gè)點(diǎn)Yi添加標(biāo)桿Cx和Cy。

其中我們要滿足Cx+Cy>=w[x][y](w[x][y]即為點(diǎn)Xi、Yi之間的邊權(quán)值)

對(duì)于一開始的初始化,我們對(duì)于每個(gè)點(diǎn)分別進(jìn)行如下操作

Cx=max(w[x][y]);

Cy=0;

然后,我們可以進(jìn)行連邊,即采用匈牙利算法,只是在判斷兩點(diǎn)之間是否有連線的條件下,因?yàn)槲覀円獙⒆畲筮呥M(jìn)行連線,所以原來判斷是否有邊的條件w[x][y]==0換成了

Cx+Cy==w[x][y]

此時(shí),有一個(gè)新的名詞——相等子圖

因?yàn)槲覀兺ㄟ^了巧妙的處理讓計(jì)算機(jī)自動(dòng)連接邊權(quán)最大的邊,換句話說,其他邊計(jì)算機(jī)就不會(huì)連了,也就“不存在”這個(gè)圖中,但我們可以隨時(shí)加上這些“不存在”圖中的邊。此時(shí)這個(gè)圖可以認(rèn)為是原圖的子圖,并且是等效。

這樣,計(jì)算機(jī)在枚舉右邊的點(diǎn)的時(shí)候,滿足以上條件,就能夠知道這條邊是我們要連的最大的邊,就能進(jìn)行連邊了。

于是乎我們連了AD。

接下來就尷尬了,計(jì)算機(jī)接下來要連B點(diǎn)的BD,但是D點(diǎn)已經(jīng)和A點(diǎn)連了,怎么辦呢???

根據(jù)匈牙利算法,我們做的是將A點(diǎn)與其他點(diǎn)進(jìn)行連線,但此時(shí)的子圖里“不存在”與A點(diǎn)相連的其他邊,怎么辦呢??

為此,我們就需要加上這些邊!

很明顯,我們添邊,自然要加上不在子圖中邊權(quán)最大的邊,也就是和子圖里這個(gè)邊權(quán)值差最小的邊。

于是,我們?cè)僖欢纫肓艘蛔兞縟,d=min{Cx[i]+Cy[j]-w[i][j]}

其中,在這個(gè)題目里Cx[i]指的是A的標(biāo)桿,Cy[j]是除D點(diǎn)(即已連點(diǎn))以外的點(diǎn)的標(biāo)桿。

隨后,對(duì)于原先存在于子圖的邊AD,我們將A的標(biāo)桿Cx[i]減去d,D的標(biāo)桿Cy[d]加上d。

這樣,這就保證了原先存在AD邊保留在了子圖中,并且把不在子圖的最大權(quán)值的與A點(diǎn)相連的邊AE添加到了子圖。

因?yàn)橛?jì)算機(jī)判斷一條邊是否在該子圖的條件是其兩端的頂點(diǎn)的標(biāo)桿滿足

Cx+Cy==w[x][y]

對(duì)于原先的邊,我們對(duì)左端點(diǎn)的標(biāo)桿減去了d,對(duì)右端點(diǎn)的標(biāo)桿加上了d,所以最終的結(jié)果還是不變,仍然是w[x][y]。

對(duì)于我們要添加的邊,我們對(duì)于左端點(diǎn)減去了d,即Cx[i]=Cx[i]-d;為方便表示我們把更改后的的Cx[i]視為Cz[i],即Cz[i]=Cx[i]-d;

對(duì)于右端點(diǎn),我們并沒有對(duì)其進(jìn)行操作。那這條我們要添加邊的兩端點(diǎn)的標(biāo)號(hào)是否滿足Cz[i]+Cy[j]=w[i][j]?

因?yàn)?/strong>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]);

化簡(jiǎn)得Cz[i]+Cy[j]=w[i][j]。

------滿足了要求!即添加了新的邊。--------

值得注意的是,這里我們只是對(duì)于一條邊操作,當(dāng)我們添加了幾條邊,要進(jìn)行如上操作時(shí),要保證原先存在的邊不消失,那么我們就要先求出了d,然后

對(duì)于每個(gè)連邊的左端點(diǎn)(記作集合S)的每個(gè)點(diǎn)的標(biāo)號(hào)減去了d之后,然后連邊的右端點(diǎn)(記作T)加上d,這樣就保證了原先的邊不消失啦~

實(shí)際上這就是一直在尋找著增廣路,通過不斷修改標(biāo)桿進(jìn)行添邊實(shí)現(xiàn)。

接下來就繼續(xù)著匈牙利算法,直到完全匹配完為止。

該算法的正確性就在于 它每次都選擇最大的邊進(jìn)行連邊

至此,我們?cè)倩仡橩M算法的步驟:

*1.用鄰接矩陣(或其他方法也行啦)來儲(chǔ)存圖。*

2.運(yùn)用貪心算法初始化標(biāo)桿。

*3.運(yùn)用匈牙利算法找到完備匹配。*

*4.如果找不到,則通過修改標(biāo)桿,增加一些邊。*

5.重復(fù)3,4的步驟,直到完全匹配時(shí)可結(jié)束。

引用鏈接
現(xiàn)在有N男N女,有些男生和女生之間互相有好感,我們將其好感程度定義為好感度,我們希望把他們兩兩配對(duì),并且最后希望好感度和最大。

怎么選擇最優(yōu)的配對(duì)方法呢?

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

這樣,我們把每個(gè)人的期望值標(biāo)出來。

接下來,開始配對(duì)。

配對(duì)方法:

我們從第一個(gè)女生開始,分別為每一個(gè)女生找對(duì)象。

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

注意:每一輪匹配每個(gè)男生只會(huì)被嘗試匹配一次!

具體匹配過程:

為女1找對(duì)象=

(此時(shí)無人配對(duì)成功)

根據(jù) “男女兩人的期望和要等于兩人之間的好感度”的規(guī)則

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

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

所以女1選擇了男3

女1找對(duì)象成功

==為女1找對(duì)象成功

為女2找對(duì)象=

(此時(shí)女1—男3)

根據(jù)配對(duì)原則,女2選擇男3

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

我們嘗試讓女1去找別人

嘗試失敗

為女2找對(duì)象失敗!

==為女2找對(duì)象失敗

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

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

任意一個(gè)參與匹配女生能換到任意一個(gè)這輪沒有被選擇過的男生所需要降低的最小值(就可以保證每個(gè)女的都可以匹配到一個(gè)男的)

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

于是,只要期望值降低1,就有妹子可能選擇其他人。所以妹子們的期望值要降低1點(diǎn)。

同時(shí),剛才被搶的男生此時(shí)非常得意,因?yàn)橛忻米觼頁(yè)屗谑撬钠谕堤岣吡?點(diǎn)(就是同妹子們降低的期望值相同)。

于是期望值變成這樣(當(dāng)然,不參與剛才匹配過程的人期望值不變)

==繼續(xù)為女2找對(duì)象=

(此時(shí)女1—男3)

女2選擇了男1

男1還沒有被配對(duì)

女2找對(duì)象成功!

==為女2找對(duì)象成功=

為女3找對(duì)象=

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

女3沒有可以配對(duì)的男生……

女3找對(duì)象失敗

==為女3找對(duì)象失敗

此輪只有女3參與匹配

此時(shí)應(yīng)該為女3降低期望值

降低期望值1的時(shí)候,女3-男3可以配對(duì),所以女3降低期望值1

==繼續(xù)為女3找對(duì)象

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

女3相中了男3

此時(shí)男3已經(jīng)有主女1,于是女1嘗試換人

女1選擇男1

而男1也已經(jīng)有主女2,女2嘗試換人

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

所以女2換人失敗

女3找對(duì)象再次失敗

==為女3找對(duì)象失敗

這一輪匹配相關(guān)人員:女1,女2,女3,男1,男3

此時(shí),只要女2降低1點(diǎn)期望值,就能換到男2

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

我們把相應(yīng)人員期望值改變一下

==還是為女3找對(duì)象

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

女3選擇了男3

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

女1換到了男1

男1已經(jīng)有主女2,女2嘗試換人

女2換人男2

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

==為女3找對(duì)象成功=

匹配成功!!!撒花~~

到此匹配全部結(jié)束

此時(shí)

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

好感度和為最大:9

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
int love[310][310];//每個(gè)女生對(duì)男生的好感度(W兩點(diǎn)之間的權(quán)值)
int exboy[310];//男生的吸引值(右標(biāo)桿)
int exgirl[310];//女生的期望值(左標(biāo)桿)
int visboy[310];//標(biāo)記是否在此次匹配中出現(xiàn)
int visgirl[310];//標(biāo)記是否在此次匹配中出現(xiàn)
int match[310];//標(biāo)記每個(gè)男生匹配到的女生編號(hào),沒有匹配到則為-1
int slack[310];//記錄每個(gè)男生想被女生匹配到最小需要多少吸引值
int n;
int Hungarian(int girl)
{
    visgirl[girl]=1;//標(biāo)記這個(gè)女生已經(jīng)在匹配中出現(xiàn)
    for(int boy=1; boy<=n; boy++)
    {
        if(visboy[boy])//男生已經(jīng)被匹配,就繼續(xù)下一個(gè)男生
        {
            continue;
        }
        int tep=exboy[boy]+exgirl[girl]-love[girl][boy];
        if(tep==0)//男生的吸引值加上女生的期望值==女生對(duì)男生的好感度(左標(biāo)桿+右標(biāo)桿==左標(biāo)桿節(jié)點(diǎn)到右標(biāo)桿節(jié)點(diǎn)的權(quán)值)
        {
            visboy[boy]=1;
            if(match[boy]==-1||Hungarian(match[boy]))
            {
                match[boy]=girl;//標(biāo)記這個(gè)男生匹配到的女生編號(hào)
                return true;
            }
        }
        else//意味著男生的吸引值不夠,記錄每個(gè)男生需要匹配到女生的最小吸引值
        {
            slack[boy]=min(slack[boy],tep);
        }

    }
    return false;
}
void KM( )
{
    memset(match,-1,sizeof(match));
    memset(exboy,0,sizeof(exboy));//開始每個(gè)男生都沒有女生與他匹配,吸引值為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]);//初始化每個(gè)女生的期望值都是最大的(初始化左標(biāo)桿)
        }
    }
    // cout<<"flag"<<endl;
    // for(int i=1;i<=n;i++)
    // {
    //     cout<<exgirl[i]<<" "<<exboy[i]<<endl;
    // }
    for(int i=1; i<=n; i++)//每一個(gè)女生開始進(jìn)行匹配
    {
        memset(slack,INF,sizeof(slack));//尋找最小的吸引值,所以初始化為正無窮
        while(1)
        {
            memset(visgirl,0,sizeof(visgirl));
            memset(visboy,0,sizeof(visboy));
            if(Hungarian(i))//找到匹配策略,跳出循環(huán)
            {
                break;
            }
//沒有找到匹配策略,于是更新女生的期望值(左標(biāo)桿),男生的吸引值(右標(biāo)桿)
            int d=INF;
            for(int j=1; j<=n; j++)
            {
                if(!visboy[j])
                {
                    d=min(d,slack[j]);//找出最小的吸引值(那個(gè)男生就可以匹配到女生啦)
                }
            }
            for(int j=1; j<=n; j++)
            {
                if(visgirl[j])//在匹配中出現(xiàn)的女生為了妥協(xié),期望值下降d(心里不甘心下降,于是盡可能的下降少一點(diǎn)期望值)
                {
                    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個(gè)男生匹配到的女生編號(hào)是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算法」的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。