P1559 运动员最佳匹配问题 最大费用最大流
P1559 运动员最佳匹配问题 费用流
题目链接
虽然思想不难, 但是写了好久, 原因是网络流都忘得差不多了, 这相当于复习了.
最大费用最大流.
炒鸡源点向男生连一条费用为0, 流量为1的边.女生向炒鸡汇点连一条费用为0,流量为1的边.所有男生向女生连一条费用为\(p[i][j] * q[j][i]\), 流量为1的边.
流量都为1可以确保一个男生对应一个女生.思想挺简单的.
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 25;
int n, s, t, ans, cnt;
int in[N], dis[N], pre[N], incf[N], head[N], p[N][N], q[N][N];
struct edge { int to, nxt, val, cost; } e[N * N + 2 * N];
void add(int x, int y, int z, int f) {
e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].val = z; e[cnt].cost = f;
}
int spfa() {
queue <int> q;
for(int i = 0;i <= t; i++) dis[i] = -1e9;
q.push(s); dis[s] = 0; in[s] = 1; incf[0] = 1e9;
while(!q.empty()) {
int x = q.front(); q.pop(); in[x] = 0;
for(int i = head[x]; i ; i = e[i].nxt) {
if(!e[i].val) continue;
int y = e[i].to;
if(dis[y] < dis[x] + e[i].cost) {
dis[y] = dis[x] + e[i].cost; pre[y] = i;
incf[y] = min(incf[x], e[i].val);
if(!in[y]) in[y] = 1, q.push(y);
}
}
}
return dis[t] != -1e9;
}
void up() {
int p = t;
while(p != s) {
int i = pre[p];
e[i].val -= incf[t]; e[i ^ 1].val += incf[t];
p = e[i ^ 1].to;
ans += e[i].cost * incf[t];
}
}
int main() {
n = read(); s = 0, t = 2 * n + 1; cnt = 1;
for(int i = 1;i <= n; i++) for(int j = 1;j <= n; j++) p[i][j] = read();
for(int i = 1;i <= n; i++) for(int j = 1;j <= n; j++) q[i][j] = read();
for(int i = 1;i <= n; i++) add(s, i, 1, 0), add(i, s, 0, 0);
for(int i = n + 1;i <= 2 * n; i++) add(i, t, 1, 0), add(t, i, 0, 0);
for(int i = 1;i <= n; i++)
for(int j = 1;j <= n; j++) add(i, j + n, 1, p[i][j] * q[j][i]), add(j + n, i, 0, -p[i][j] * q[j][i]);
while(spfa()) up();
printf("%d", ans);
return 0;
}