圖論之匹配
匹配的定義
給定圖 G=(V,E)
一個匹配 M 為 G 的一個子圖,使的其中的邊,兩兩不相交

一些名詞
未匹配點
未匹配邊
交替路徑
增廣路徑
交替路徑

增廣路徑

極大匹配
一個匹配 M 使得無法被加入任何新的匹配

最大匹配
所有匹配中,包含最多匹配邊的那個

二分圖最大匹配
匈牙利演算法
對稱差集
集合 A,B 的對稱差集
A⊕B=A∩B−A∪B
兩個匹配的對稱差集
M⊕M∗=P
- 孤點
- 交替路徑
- 交替環
兩個匹配的轉換
M⊕P=M∗
M∗⊕P=M
Berge's lemma
在圖 G=(V,E) 上,
一個匹配 M為最大匹配
若且唯若找不到增廣路徑
尋找增廣路
紀錄每個點的匹配點
從未匹配點開始 DFS
沿著交錯路徑走
找到新的未匹配點就找到了增廣路
實做
const int maxn = 500;
vector<int> edge[maxn];
int ma[maxn], T, vis[maxn], n, m;
// 1 ~ n : left, n+1 ~ m : right
bool dfs(int now) {
if (exchange(vis[now], T) == T) return false;
for (int u : edge[now])
if (!ma[u] || dfs(ma[u]))
return ma[u] = now, ma[now] = u;
return false;
}
int get_max_matching() {
int res = 0;
for (int i = 1;i <= n;++i)
res += (!ma[i] && (++T, dfs(i)));
return res;
}
一些優化
把找擴充路徑改成 BFS
複雜度變成 O(∣V∣∣E∣)
複雜度
找擴充路徑 O(∣E∣))
整體複雜度 O(∣V∣∣E∣)
其他相關的事情
∣最大點獨立集∣+∣最小點覆蓋∣ =∣最大邊獨立集∣+∣最小邊覆蓋∣=∣V∣
- ∣最大點獨立集∣=∣V∣−∣M∣
- ∣最大邊獨立集∣=∣M∣
- ∣最小點覆蓋∣=∣M∣
- ∣最小邊覆蓋∣=∣V∣−∣M∣
- 最大點獨立集
- 最大邊獨立集
- 最小點覆蓋
- 最小邊覆蓋
習題
CS acadmy
Flipping Matrtix
給一個 n x n,n≤103 的 01 矩陣
每次操作可交換任兩行,或任兩列
最多做 n 次操作,
如果有解
請輸出一組操作使的主對角線上面都是 1
TIOJ 1253
給 n x n,(n≤1000)的棋盤
上面有一些怪物
每次操作可以選一個橫排,或直排,把上面的怪物打掉
問至少要幾次操作才能消除所有的怪物
TIOJ 1601
在 n x m,(n,m≤200)的鏡子上
0 代表破裂, 1 代表完好
每次操作可以交換兩個橫排,或兩個直排
可以做無限次操作。
定義一個完美的鏡子為一個鏡子使的它內部都完好
求做完操作後,週長最長的完美子鏡子
***每面輸入的鏡子最外面一整圈皆無破損,即皆為1
TIOJ 1069
外送公司接到了 n,n≤103 筆訂單
第 i 筆為 xi,yi,ti 代表在座標 xi,yi 上有人會在 ti 的時候領取到食物(外送員必須在那裡等待)
已知在兩組座標移動的時間為他們的曼哈頓距離,外送員可帶無限多的食物,並且處理多個訂單
請問你至少要派多少外送員才能滿足所有訂單
(派出一個外送員時,他可以瞬間出現在你想要的地方)
一般圖匹配
縮花演算法
花


處理花的方法
- 用個 DSU 把他縮起來
(偏麻煩 - 直接處理好一些資訊,讓我們可以回朔出增廣路
(較好做
實做
int ma[maxn], lst[maxn], side[maxn], fid[maxn], n;
vector<int> edge[maxn];
void augment(int x, int y) {
while (x) {
int pa = ma[x];
ma[x] = y, ma[y] = x;
y = pa, x = lst[y];
}
}
實做
int find_lca(int x, int y) {
static int T, vis[maxn];
for (++T; ; swap(x, y)) {
if (x && exchange(vis[x], T) == T)
return x;
x = fid[lst[ma[x]]];
}
return -1;
}
bool bfs(int rt) {
#define qpush(x) (side[x] = 0, togo.push(x))
memset(side, -1, sizeof(side));
queue<int> togo;
qpush(rt);
auto flower = [&](int x, int y, int lca) {
while (x != lca) {
if (side[x] == 1) qpush(x);
lst[x] = y;
fid[x] = fid[y] = lca;
x = ma[x], y = lst[x];
}
};
while (!togo.empty()) {
int now = togo.front();
togo.pop();
for (int u : edge[now])
if (side[u] == -1) {
side[u] = 1;
lst[u] = now;
if (!ma[u])
return augment(now, u), true;
qpush(ma[u]);
}
else if (!side[u]) {
int lca = find_lca(u, now);
flower(u, now, lca);
flower(now, u, lca);
}
}
return false;
}
int max_matching() {
iota(fid, fid+n+1, 0);
int res = 0;
for (int i = 1;i <= n;++i)
res += !ma[i] && bfs(i);
return res;
}
實做參考來源:
日月掛長的 slides
圖論之匹配
By Kevin Zhang
圖論之匹配
- 1,016