二分圖的模樣 :
亂侃一通 :從字面上來理解,二分圖確定是兩部分,既然是兩部分那麼這兩部分確定各自獨立,而後經過必定
的關係進行創建鏈接。
理論上來講 : 若是一張無向圖的 N 個節點(N >= 2) 能夠被分紅 A,B 兩個非空集合,其中 A 交 B = 空集,
而且在同一集合內的點之間都沒有邊相連,那麼這張無向圖就是一張二分圖,A,B分別稱爲二分圖
的左部和右部。
看個圖:
根據上圖,咱們來侃侃一些概念 :
匹配 : 「任意兩條邊都沒有公共端點」 的邊的集合被稱爲圖的一組匹配(圖中的1 - 6,,2 - 7 )。
最大匹配:包含邊數最多的一組被稱爲二分圖的最大匹配 。 (上圖的匹配就是最大匹配)
增廣路徑 : 該算法也被稱爲增廣路算法,每次從左部的點找與右部匹配的點時,咱們須要去找一條增廣路徑,若是
該路徑存在,說明能夠將目前的全部點進行匹配,反之則右部則沒有與之左部相匹配的點。
具體講解可參考這位大佬的博客,解釋的比較形象:https://blog.csdn.net/dark_scope/article/details/8880547
#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; // 與本身相關聯的點都已經名花有主了,說明這條路行不通了
}
有不懂得地方,歡迎留言!!!