求助二分图匹配+拓扑排序做法
查看原帖
求助二分图匹配+拓扑排序做法
120324
Yansuan_HCl楼主2022/2/14 16:19

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 的点即有环的。这种做法问题出在哪?

2022/2/14 16:19
加载中...