洛谷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;
}
浙公网安备 33010602011771号