RT, 做法详见注释。
#include <bits/stdc++.h>
using namespace std;
const int N = 200005, E = 800005;
const int INF = 0x7FFFFFFF;
int n, m, s, t;
int tail[N], to[E], weight[E], pre[E], ptr = 1;
void add(int u, int v, int w) {
to[++ptr] = v;
weight[ptr] = w;
pre[ptr] = tail[u];
tail[u] = ptr;
}
int dep[N];
bool bfs() {
memset(dep, 0, sizeof(dep));
queue<int> q; q.push(s); dep[s] = 1;
while (q.size()) {
int u = q.front(); q.pop();
for (int p = tail[u]; p; p = pre[p]) {
int v = to[p], w = weight[p];
if (dep[v] || !w) continue;
dep[v] = dep[u] + 1;
q.push(v);
}
}
return dep[t];
}
int dinic(int u, int flow) {
if (u == t) return flow;
int ans = 0;
for (int p = tail[u]; p && flow; p = pre[p]) {
int v = to[p], w = weight[p];
if (!w || dep[v] != dep[u] + 1) continue;
int k = dinic(v, min(w, flow));
flow -= k; weight[p] -= k;
ans += k; weight[p ^ 1] += k;
}
if (!ans) dep[u] = 0;
return ans;
}
int x[N], y[N];
pair<int, int> pt[N];
int main() {
scanf("%d%d", &n, &m); s = 1; t = n + m + 2;
for (int i = 1; i <= n; ++i) {
int u, v; scanf("%d%d", &u, &v);
u += n + 1; v += n + 1;
x[i] = u; y[i] = v;
add(i + 1, u, 1); add(u, i + 1, 0);
add(i + 1, v, 1); add(v, i + 1, 0);
}
for (int i = 1; i <= n; ++i) { add(s, i + 1, 1); add(i + 1, s, 0); }
for (int j = 1; j <= m; ++j) { add(j + 1 + n, t, 1); add(t, j + 1 + n, 0); }
int ans = 0;
while (bfs())
ans += dinic(s, INF);
printf("%d\n", n - ans);
// 二分图匹配,转化为最大流
// 让只能选次选的奶牛紧跟在选掉首选的奶牛后面
// 另外建图,如果奶牛匹配到首选麦片,将奶牛与首选麦片连边,保证该奶牛先于其他占用首选麦片的奶牛
// 否则如果匹配到二选麦片,首选麦片指向这只奶牛,这只奶牛指向次选麦片,保证这只奶牛的次序在占用首选的奶牛之后,先于其他占用该奶牛次选的奶牛
// 这样要么是左部点向右部点连边,要么是右部点向左部点连边之后再将左部点向右部点连边
// 如果出现环,就说明需求次序出现了环,而显然有问题
vector<int> g[N]; int deg[N]; memset(deg, 0, sizeof(deg));
for (int i = 2; i <= n + 1; ++i) {
bool f1 = false, f2 = false;
for (int p = tail[i]; p; p = pre[p]) {
int v = to[p], w = weight[p];
if (v == x[i - 1] && !w) f1 = true;
if (v == y[i - 1] && !w) f2 = true;
}
if (f1) { g[i].push_back(x[i - 1]); ++deg[x[i - 1]]; }
if (f2) { g[x[i - 1]].push_back(i); ++deg[i]; g[i].push_back(y[i - 1]); ++deg[y[i - 1]]; }
if (!(x[i - 1] > n + 1)) return 1;
if (!(y[i - 1] > n + 1)) return 2;
// clog << f1 << ' ' << f2 << endl;
}
// for (int i = s; i <= t; ++i)
// clog << deg[i] << ' ';
// clog << endl;
queue<int> q;
for (int i = s; i <= t; ++i) if (!deg[i]) q.push(i);
int cnt = 0;
while (q.size()) {
int u = q.front(); q.pop();
++cnt;
if (u >= 2 && u <= n + 1) printf("%d\n", u - 1);
for (int v : g[u]) if (!(--deg[v])) q.push(v);
}
// for (int i = 2; i <= n + 1; ++i) if (deg[i]) printf("%d\n", i);
if (cnt != t) return -1; // 建图出现了环,为什么?
return 0;
}
提交记录中 RE 的点即有环的。这种做法问题出在哪?