n×m 棋盤,部分格子可放車。車不可同行同列。求最大放車數 (最大匹配),及有多少格子是「必須放車」才能達到最大匹配 (關鍵邊)。
(x,y) 連接行 x 和列 y。max_match。邊 (x,y) 在某個最大匹配中。判斷是否為關鍵邊 (即所有最大匹配都包含它):
O(E * V*E),小數據可過。(x,y):刪除該邊,使 y 成為自由點。x 出發尋找增廣路。若找到,說明存在不含 (x,y) 的最大匹配,故非關鍵;若找不到,則為關鍵邊。O(E * (V+E)) 總體更快。#include <bits/stdc++.h>
using namespace std;
const int MAXN = 105;
bool g[MAXN][MAXN];
int matchR[MAXN]; // 列匹配的行
bool vis[MAXN];
int n, m;
bool dfs(int u) {
for (int v = 1; v <= m; ++v) if (g[u][v] && !vis[v]) {
vis[v] = true;
if (matchR[v] == -1 || dfs(matchR[v])) {
matchR[v] = u;
return true;
}
}
return false;
}
int hungarian() {
fill(matchR, matchR+m+1, -1);
int cnt = 0;
for (int u = 1; u <= n; ++u) {
fill(vis, vis+m+1, false);
if (dfs(u)) ++cnt;
}
return cnt;
}
int main() {
int k, tc = 0;
while (cin >> n >> m >> k) {
memset(g, 0, sizeof(g));
while (k--) { int x,y; cin>>x>>y; g[x][y]=true; }
int max_match = hungarian();
int critical = 0;
// 記錄一個最大匹配的匹配邊
vector> matched;
for (int v = 1; v <= m; ++v)
if (matchR[v] != -1) matched.emplace_back(matchR[v], v);
for (auto [x,y] : matched) {
g[x][y] = false;
matchR[y] = -1;
fill(vis, vis+m+1, false);
if (!dfs(x)) ++critical; // 找不到增廣路 -> 關鍵邊
g[x][y] = true;
matchR[y] = x; // 恢復
}
cout << "Board " << ++tc << " have " << critical
<< " important blanks for " << max_match << " chessmen.\n";
}
return 0;
}
dfs 找增廣路,matchR 記錄右部匹配。Board X have Y important blanks for Z chessmen.二分圖最大匹配 + 關鍵邊判定是經典組合。增廣路法避免了重複跑完整匈牙利,是判定「唯一匹配邊」的標準技巧。