洛谷P3967 [TJOI2014] 匹配 题解 二分图最优匹配/最大费用最大流

题目链接:https://www.luogu.com.cn/problem/P3967

解题思路:

完全来自 7KByte大佬的博客,特别是博客中的以下这句话:

但如果我们直接枚举每条边删除再跑费用流,并不能再规定时间内跑完。经过观察我们发现,跑第一问的费用流时,一共只有n条男生到女生的边有流量。所以其他的男生到女生的边显然不可能为必经边。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 170, maxm = maxn * maxn * 2 + 5, inf = 0x3f3f3f3f;

struct Edge {
    int v, w, c, nxt;
} edge[maxm];
int n, m, s, t, head[maxn], ecnt;

int H[85][85];

void add_edge(int u, int v, int w, int c) {
    edge[ecnt] = { v, w, c, head[u] }; head[u] = ecnt++;
    edge[ecnt] = { u, 0, -c, head[v] }; head[v] = ecnt++;
}

void init(int x = -1, int y = -1) {
    ecnt = 0;
    fill(head+1, head+2*n+3, -1);
    s = 2 * n + 1;
    t = 2 * n + 2;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == x && j == y)
                continue;
            add_edge(i, j+n, 1, H[i][j]);
        }
    }
    for (int i = 1; i <= n; i++) {
        add_edge(s, i, 1, 0);
        add_edge(i+n, t, 1, 0);
    }
}

int dis[maxn], cur[maxn], sum_cost;
bool vis[maxn];

bool spfa() {
    queue<int> que;
    fill(dis+1, dis+2*n+3, -inf);
    dis[s] = 0;
    vis[s] = true;
    que.push(s);
    while (!que.empty()) {
        int u = que.front();
        que.pop();
        vis[u] = false;
        for (int i = head[u]; ~i; i = edge[i].nxt) {
            auto &[v, w, c, nxt] = edge[i];
            if (w > 0 && dis[u] + c > dis[v]) {
                dis[v] = dis[u] + c;
                if (!vis[v]) {
                    vis[v] = true;
                    que.push(v);
                }
            }
        }
    }
    return dis[t] > -inf;
}

vector<pair<int, int>> key_paths;

int dfs(int u, int flow) {
    if (u == t)
        return flow;
    vis[u] = true;
    int ans = 0;
    for (int &i = cur[u]; ~i && ans < flow; i = edge[i].nxt) {
        auto &[v, w, c, nxt] = edge[i];
        if (!vis[v] && w > 0 && dis[v] == dis[u] + c) {
            int f = dfs(v, min(w, flow - ans));
            if (f > 0) {
                ans += f;
                sum_cost += f * c;
                edge[i].w -= f;
                edge[i^1].w += f;
            }
        }
    }
    vis[u] = false;
    return ans;
}

int dinic() {
    int ans = 0;
    while (spfa()) {
        copy(head+1, head+2*n+3, cur+1);
        int f = dfs(s, inf);
        ans += f;
    }
    return ans;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            scanf("%d", &H[i][j]);
    init();
    dinic();
    int ans = sum_cost;
    printf("%d\n", sum_cost);
    for (int u = 1; u <= n; u++) {
        for (int i = head[u]; ~i; i = edge[i].nxt) {
            int v = edge[i].v, w = edge[i].w;
            if (n+1 <= v && v <= 2*n && w == 0)
                key_paths.push_back({u, v-n});
        }
    }

    for (auto [x, y] : key_paths) {
        sum_cost = 0;
        init(x, y);
        dinic();
        if (ans > sum_cost) {
            printf("%d %d\n", x, y);
        }
    }

    return 0;
}
posted @ 2026-04-27 20:32  quanjun  阅读(20)  评论(0)    收藏  举报