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;
}
posted @ 2020-11-14 20:53  C锥  阅读(98)  评论(0编辑  收藏  举报