二分图左右点集A,B。
建立超级原点s,与集合A中的每个点建立一条由s指向a的边,长度为一;
建立超级终点t,与集合B中的每个点建立一条由b指向t的边,长度为一;
从s到t的最大流即为二分图的最大匹配。
优化:
· cur数组记录当前的点流向了哪个子点,避免重复从头遍历
· dfs中,flow<f时,break
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 3e4 + 5;
struct edge {
int to, cap, rev;
};
vector<edge> G[N];
int dep[N], cur[N];
int n, m, s, t;
void add_edge(int from, int to, int cap) {
edge a = { to,cap,G[to].size() };
G[from].push_back(a);
edge b = { from,0,G[from].size() - 1 };
G[to].push_back(b);
}
bool bfs() { //将图分层,并判断能否到达终点
memset(dep, 0, sizeof dep); //每次都要初始化
memset(cur, 0, sizeof cur);
queue<int> q; q.push(s);
dep[s] = 1;
while (q.size()) {
int u = q.front(); q.pop();
for (int i = 0; i < G[u].size(); i++) {
edge& e = G[u][i];
if (e.cap && !dep[e.to]) {
dep[e.to] = dep[u] + 1;
q.push(e.to);
}
}
}
return dep[t];
}
int dfs(int v, int t, int f) {
if (v == t) return f;
int flow = 0;
for (int i = cur[v]; flow < f && i < G[v].size(); i++) {
cur[v] = i;
edge& e = G[v][i];
if (e.cap && dep[e.to] == dep[v] + 1) { //只能走下一层
int d = dfs(e.to, t, min(f, e.cap));
if (d > 0) {
e.cap -= d;
G[e.to][e.rev].cap += d;
flow += d;
}
}
}
if (flow == 0) dep[v] = 0; //这条路不能走了直接封锁点v,节约时间
return flow;
}
int max_flow(int s, int t) {
int flow = 0;
while (bfs()) {
flow += dfs(s, t, INF);
}
return flow;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
s = 0, t = 30000;
for (int i = 1; i <= n; i++) {
add_edge(s, i, 1);
int num; cin >> num;
for (int j = 0; j < num; j++) {
int v; cin >> v;
add_edge(i, v + 10000, 1);
}
}
for (int i = 1; i <= m; i++) add_edge(i + 10000, t, 1);
cout << m - max_flow(s, t) << endl;
return 0;
}