Kuhn-Munkras算法解決二分圖最優權值匹配


  在看這篇博文之前建議看一下上一篇匈牙利法解決二分圖最大匹配問題:

  https://www.cnblogs.com/fangxiaoqi/p/10808729.html

  這篇博文參考自:https://www.cnblogs.com/logosG/p/logos.html

最優匹配

  我們這里先說一下什么叫做最優匹配(也被稱作最大帶權分配)。

  簡而言之,最優匹配就是指在帶權邊的二分圖中,求一個匹配使得匹配邊上的權值和最大。

  

兩個例子

例子1

  出自wenr博客: http://www.cnblogs.com/wenruo/p/5264235.html

  這也是一個拯救單身的例子。紅線上的權值表示女生對男生的好感度,至於單身男青年,對於女生的期望為0(有妹子就行,不挑),最優權值匹配即好感度之和最大。

  

  一:將女生的最大期望和男生的期望標出來,開始匹配。

  

  ==========================女1==========================

  女1 X 男1 : 4 + 0 != 3  不匹配;

  女1 X 男2:  4 + 0  = 4  匹配

  成功脫單。

  ==========================女2==========================

  女2 X 男1: 3 + 0 != 2 不匹配;

  女2 X 男2: 3 + 0 != 1 不匹配;

  女2 X 男3: 3 + 0  = 3  匹配;

  但是!男3 已經 名草有主了。

  這時,男3 是個香餑餑,妹紙們都搶着要。男3對女生的期望 +1 ,而為了更好的分配,搶男生的妹子們要對男生的要求要降低一點

  局勢就變成這樣了:

  

  二:接下來就是一個遞歸的過程嘍(下面的也是)

  ==========================女2==========================

  女2 X 男1: 2 + 0 != 2  匹配;

  成功脫單。

  ==========================女3==========================

  女3 X 男3: 5 + 1 != 5  不匹配;

  然后妹子3只能降低標准了。

  

  ==========================女3==========================

  女3 X 男3: 4 + 1 != 5  匹配;

  但是!男3有了吖。好叭,男3再次增加期望(渣男),妹子3和妹子1把要求降低一點。

  妹子1嘗試換人,去和妹子2搶男1。

  但是!男1也有了吖。好叭,男1的期望+1,妹子2的期望降低一點。

  妹子2嘗試換人,去和男2匹配。

  成功脫單。

  

  最后,女1 X 男1,女2 X 男2,女3 X 男3 為最優匹配,大團圓結局。

  而對於這種問題我們為什么要用KM算法?這顯然是可以看出來的。但是,當單身男女數量越來越多,很難直接看出,而且如果僅僅用匈牙利算法找最大匹配然后比較每個最大匹配的權值來找最優匹配的實行難度也就越來越大,使用KM算法是一種好的選擇。從上面的例子我們可以看出,KM算法是基於匈牙利方法進行不斷進行遞歸和貪心的。

 

例子2

  出自 https://www.cnblogs.com/logosG/p/logos.html

  這是一個公司任免問題。

  在一家公司里,有員工A,B,C,有三種工作a,b,c,如果員工和工作之間有線相連,則代表員工能勝任這份工作,而每個員工做每份工作的效率各不相同,圖中的權值代表工作效率,而我們的目的就是做合適的安排保證最大的效率。

  

  標出最大權值和右側的期望。

  

  ==========================安排A==========================

  A--a: 4 + 0 != 3  不匹配;

  A--c: 4 + 0  = 4     匹配;

  ==========================安排B==========================

  B--a: 3 + 0 != 2  不匹配;

  B--b: 3 + 0 != 1  不匹配;

  B--c: 3 + 0 != 3     匹配;但是產生沖突。

  進行 -1 和 +1 的操作,來波遞歸。

  

  B--a: 2 + 0 = 2   匹配。

  ==========================安排C==========================

  C--c: 5 + 1 != 5   不匹配;  對C減1

  

  C--c: 4 + 1 = 5   匹配但沖突;(情形類似於例1,遞歸和貪心即可)

  最終,A--a,B--b,C--c。  

KM算法的思想

  KM算法是對匈牙利算法的一種貪心擴展,而其思想是通過給每個頂點一個標號(叫做頂標)來把求完備匹配(匈牙利算法求出完備匹配)下的最大權匹配問題(即最優分配)。通過修改某些點的符號(但要滿足點標始終是可行的),不斷增加圖中可行邊總數,直到圖中存在僅由可行邊組成的完全匹配為止,此時這個匹配一定是最佳的。

  具體說一下最優匹配——定理:設$M$是一個帶權完全二分圖$G$的一個完備匹配,給每個頂點一個可行頂標(第$i$個$x$頂點的可行頂標用 $lx[i]$表示,第$j$個$y$頂點的可行頂標用 $ly[j]$ 表示),如果對所有的邊$(i,j)$ in G,都有 $lx[i] + ly[j] \ge w[i,j]$ 成立($w\left[ {i,j} \right]$ 表示邊的權),且對所有的邊$(i,j)$ $in$ $M$,都有 $lx\left[ i \right] + ly\left[ j \right] = w\left[ {i,j} \right]$ 成立,則$M$是圖$G$的一個最優匹配。

KM算法的步驟

  1. 用鄰接矩陣(或其他方法也行啦)來儲存圖,注意:如果只是想求最大權值匹配而不要求是完全匹配的話,請把各個不相連的邊的權值設置為0。
  2. 運用貪心算法初始化標桿。
  3. 運用匈牙利算法找到完備匹配。
  4. 如果找不到,則通過修改標桿,增加一些邊。
  5. 重復3,4的步驟,直到完全匹配時可結束。

KM算法的流程

  (1)初始化可行頂標的值 (設定$lx$,$ly$的初始值) 。

  (2)用匈牙利算法尋找相等子圖的完備匹配。  

  (3)若未找到增廣路則修改可行頂標的值。

  重復(2)(3)直到找到相等子圖的完備匹配為止。

 

參考自:http://www.cnblogs.com/zpfbuaa/p/7218607.html

 

 

hdoj 2255 奔小康賺大錢

  我們以HDOJ2255為例。http://acm.hdu.edu.cn/showproblem.php?pid=2255

奔小康賺大錢

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 15718    Accepted Submission(s): 6746

  Problem Description
  傳說在遙遠的地方有一個非常富裕的村落,有一天,村長決定進行制度改革:重新分配房子。
  這可是一件大事,關系到人民的住房問題啊。村里共有n間房間,剛好有n家老百姓,考慮到每家都要有房住(如果有老百姓沒房子住的話,容易引起不安定因素),每家必須分配到一間房子且只能得到一間房子。
  另一方面,村長和另外的村領導希望得到最大的效益,這樣村里的機構才會有錢。由於老百姓都比較富裕,他們都能對每一間房子在他們的經濟范圍內給出一定的價格,比如有3間房子,一家老百姓可以對第一間出10萬,對第2間出2萬,對第3間出20萬(當然是在他們的經濟范圍內)。.現在這個問題就是村領導怎樣分配房子才能使收入最大(村民即使有錢購買一間房子但不一定能買到,要看村領導分配的)?

  Input

  輸入數據包含多組測試用例,每組數據的第一行輸入n,表示房子的數量(也是老百姓家的數量),接下來有n行,每行n個數表示第 i 個村民對第 j 間房出的價格(n<=300)。
  Output
  請對每組數據輸出最大的收入值,每組的輸出占一行。
  Sample Input
  2
  100 10
  15 23
  Sample Output
  123
  水題一道,直接套模板即可。
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;

int love[305][305];    // 每個妹子對每個男生的好感度 
int ex_girl[305];      // 每個妹子的期望值
int ex_boy[305];       // 每個男生的期望值
bool vis_girl[305];    // 每一輪匹配匹配過的女生
bool vis_boy[305];     // 每一輪匹配匹配過的男生
int match[305];        // 每個男生匹配到的妹子 如果沒有則為-1
int slack[305];        // 每個漢子如果能被妹子傾心最少還需要多少期望值
int n;

bool dfs(int girl){
    vis_girl[girl] = true;
    for (int boy = 0; boy < n; boy++) {
        if (vis_boy[boy]) continue; // 每一輪匹配 每個男生只嘗試一次
        int gap = ex_girl[girl] + ex_boy[boy] - love[girl][boy];
        if (gap == 0) {  // 如果符合要求
            vis_boy[boy] = true;
            if (match[boy] == -1 || dfs( match[boy] )) {    // 找到一個沒有匹配的男生 或者該男生的妹子可以找到其他人
                match[boy] = girl;
                return true;
            }
        }else{
            slack[boy] = min(slack[boy], gap);  // slack 可以理解為該男生要得到女生的傾心 還需多少期望值 取最小值 備胎的樣子
        }
    }
    return false;
}
int KM(){
    memset(match, -1, sizeof match);    // 初始每個男生都沒有匹配的女生
    memset(ex_boy, 0, sizeof ex_boy);   // 初始每個男生的期望值為0
    // 每個女生的初始期望值是與她相連的男生最大的好感度
    for (int i = 0; i < n; i++) {
        ex_girl[i] = love[i][0];
        for (int j = 1; j < n; j++) {
            ex_girl[i] = max(ex_girl[i], love[i][j]);
        }
    }
    // 嘗試為每一個女生解決歸宿問題
    for (int i = 0; i < n; i++) {
        fill(slack, slack + n, INF);    // 因為要取最小值 初始化為無窮大
        while(1){
            // 為每個女生解決歸宿問題的方法是 :如果找不到就降低期望值,直到找到為止
            // 記錄每輪匹配中男生女生是否被嘗試匹配過
            memset(vis_girl, false, sizeof vis_girl);
            memset(vis_boy, false, sizeof vis_boy);
            if(dfs(i)) break;  // 找到歸宿 退出
            // 如果不能找到 就降低期望值
            // 最小可降低的期望值
            int d = INF;
            for (int j = 0; j < n; j++)
                if (!vis_boy[j])    d = min(d, slack[j]);
            for (int j = 0; j < n; j++) {
                // 所有訪問過的女生降低期望值
                if (vis_girl[j]) ex_girl[j] -= d;
                // 所有訪問過的男生增加期望值
                if (vis_boy[j]) ex_boy[j] += d;
                // 沒有訪問過的boy 因為girl們的期望值降低,距離得到女生傾心又進了一步!
                else slack[j] -= d;
            }
        }
    }
    // 匹配完成 求出所有配對的好感度的和
    int res = 0;
    for (int i = 0; i < n; i++)
        res += love[match[i]][i];
    return res;
}
int main(){
    while (cin>>n) {
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                cin>>love[i][j];
        cout<<KM()<<endl;
    }
    return 0;
}        

 

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM