# Matching 演算法
## 二分圖最大匹配
- 給定一個不完美的二分圖,找出最大匹配數
- 可以直接用 flow 觀點,增加兩個點 source 及 destination 解決
- 演算法即為不斷找出增廣路徑
- `最小點覆蓋 = 最大匹配`
```clike=
#include <bits/stdc++.h>
using namespace std;
vector <int> G[100];
bool visited[100];
int match[100]; // 只記錄右側的點 matching
bool dfs(int now){
for(auto &nb:G[now]){
if(visited[nb]) continue;
visited[nb] = true;
if(match[nb]==-1 || dfs(match[nb])){ // 鄰居還沒匹配,或可以修改
// 注意是對 match[nb] 做 DFS
match[nb] = now;
return true;
}
}
return false;
}
int bipartite_matching(int n){
int ans = 0;
memset(match, -1, sizeof(match));
for(int x = 1; x <= n; x++){ // 只對左側的點找增廣
memset(visited,false,sizeof(visited));
if(dfs(x)) ans++;
for(int i=1;i<=8;i++) cout<<match[i]<<" "; cout<<endl;
}
return ans;
}
int main(){
vector<vector<int>> input = {{1,5},{1,7},{2,5},{3,5},{3,6},{4,7},{4,8}};
for(auto e:input){
G[e[0]].push_back(e[1]);
G[e[1]].push_back(e[0]);
}
cout<<bipartite_matching(4)<<endl;
return 0;
}
```
- 類似作法可以推廣到一般圖最大匹配,但細節過於複雜
## 完美二分圖最大權匹配
- The Hungarian matching algorithm / Kuhn-Munkres algorithm
- https://brilliant.org/wiki/hungarian-matching/#
- https://omeletwithoutegg.github.io/2020/11/16/Maximum-Weight-Bipartite-Matching/
- https://www.cc.gatech.edu/~rpeng/18434_S15/hungarianAlgorithm.pdf
```clike=
#include <bits/stdc++.h>
using namespace std;
const int N = 125;
int n;
int g[N][N];
int lx[N], ly[N]; // labeling function
int visx[N], visy[N];
int match[N]; // match[j] = i
void addEdge(int a, int b, int weight) {
g[a][b] = max(g[a][b], weight);
}
bool dfs(int i) {
if(visx[i]) return false;
visx[i] = true;
for(int j = 0; j < n; j++) {
if(visy[j]) continue;
if(lx[i] + ly[j] - g[i][j] != 0) continue;
visy[j] = true;
if(match[j] == -1 || dfs(match[j])) {
match[j] = i;
return true;
}
}
return false;
}
bool augment(int x) { // 從 x 出發,沿著 Gl 找增廣路徑
for(int i = 0; i < n; i++) visx[i] = visy[i] = false;
return dfs(x);
}
void relabel() { // 找不到增廣路徑時,調整 Gl
int delta = 1e9;
for(int i = 0; i < n; i++) if(visx[i])
for(int j = 0; j < n; j++) if(!visy[j])
delta = min(delta, lx[i] + ly[j] - g[i][j]);
for(int i = 0; i < n; i++) if(visx[i]) lx[i] -= delta;
for(int j = 0; j < n; j++) if(visy[j]) ly[j] += delta;
}
int solve() {
for(int i = 0; i < n; i++) {
lx[i] = ly[i] = 0;
for(int j = 0; j < n; j++)
lx[i] = max(lx[i], g[i][j]);
}
for(int i = 0; i < n; i++) match[i] = -1;
// The Key algorithm
for(int i = 0; i < n; i++){
while(!augment(i)) // 為 i 找到 matching
relabel(); // 如果當下找不到,則每次 relabel ,可以把一個 y_j 納入考慮
// 因此 n 次 relabel,必定都納入考慮,必定可以增廣
cout<<"--------\n";
for(int ii = 0; ii < n; ii++) cout<<lx[ii]<<((ii==n-1)?'\n':' ');
for(int jj = 0; jj < n; jj++) cout<<ly[jj]<<((jj==n-1)?" -> ":" ");
for(int jj = 0; jj < n; jj++) cout<<match[jj]<<((jj==n-1)?"\n":" ");
}
int ans = 0;
for(int j = 0; j < n; j++) if(match[j] != -1) ans += g[match[j]][j];
return ans;
}
int main(){
n = 4;
addEdge(0,0,3);addEdge(0,1,4);addEdge(0,2,5);addEdge(0,3,6);
addEdge(1,0,5);addEdge(1,1,3);addEdge(1,2,4);addEdge(1,3,7);
addEdge(2,0,6);addEdge(2,1,5);addEdge(2,2,6);addEdge(2,3,3);
addEdge(3,0,2);addEdge(3,1,8);addEdge(3,2,5);addEdge(3,3,7);
cout<<solve();
return 0;
}
```