每日一侃----二分圖最大匹配(匈牙利算法)

二分圖的模樣 :

亂侃一通   :從字面上來理解,二分圖確定是兩部分,既然是兩部分那麼這兩部分確定各自獨立,而後經過必定
             的關係進行創建鏈接。
 理論上來講 : 若是一張無向圖的 N 個節點(N >= 2) 能夠被分紅 A,B 兩個非空集合,其中 A 交 B = 空集,
             而且在同一集合內的點之間都沒有邊相連,那麼這張無向圖就是一張二分圖,A,B分別稱爲二分圖
             的左部和右部。

看個圖:

根據上圖,咱們來侃侃一些概念 :

匹配 : 「任意兩條邊都沒有公共端點」 的邊的集合被稱爲圖的一組匹配(圖中的1 - 6,,2 - 7 )。
最大匹配:包含邊數最多的一組被稱爲二分圖的最大匹配 。 (上圖的匹配就是最大匹配)

增廣路徑 : 該算法也被稱爲增廣路算法,每次從左部的點找與右部匹配的點時,咱們須要去找一條增廣路徑,若是
          該路徑存在,說明能夠將目前的全部點進行匹配,反之則右部則沒有與之左部相匹配的點。
           具體講解可參考這位大佬的博客,解釋的比較形象:https://blog.csdn.net/dark_scope/article/details/8880547

Code PK Self

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int SIZE = 1e5 + 10;
int match[SIZE],vis[SIZE];                    // match : 存儲兩個部分之間的匹配關係 vis : 標記是否訪問過某個點
int head[SIZE],ver[SIZE],Next[SIZE];                // 存儲兩個部分之間的關係

int n1,n2,m,tot;
int u,v;
 
int main(void) {
    void add(int u,int v);
    bool DFS(int u);
    scanf("%d%d%d",&n1,&n2,&m);
    for(int i = 1; i <= m; i ++) {
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    int res = 0;
    for(int i = 1; i <= n1; i ++) {
        memset(vis,0,sizeof(vis)); // 每次都是從左部一個新的點出發,因此每次都須要進行Clear,咱們只須要關注 match 數組便可
        if(DFS(i)) res ++;         // 左部和右部能夠匹配的數量 + 1
    }
    cout << res << endl;
    return 0;
}

void add(int u,int v) {
    ver[ ++ tot] = v,Next[tot] = head[u],head[u] = tot;
    return ;
}

bool DFS(int u) {
    for(int i = head[u]; i; i = Next[i]) {
        int y = ver[i];
        if(!vis[y]) {
            vis[y] = 1;
            if(!match[y] || DFS(match[y])) {  
                           // 有能夠直接匹配的或者經過找到一條增廣路徑的匹配點均可以
                match[y] = u;  
                           // 深搜回溯是,正好把路徑上的狀態取反,就會將因此的邊都匹配
                return true;
            }
        }
    }
    return false;     // 與本身相關聯的點都已經名花有主了,說明這條路行不通了
}

有不懂得地方,歡迎留言!!!

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息