[ SDOI 2017 ] 新生舞会
题目
思路
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 410, M = 500010;
const double eps = 1e-7;
int n, m, S, T, b[N][N], a[N][N];
// 前向星
int h[N], ptr[M], val[M], f[M], idx;
// 边权
double w[M];
void add(int a, int b, int c, double d) { val[idx] = b, w[idx] = d, f[idx] = c, ptr[idx] = h[a], h[a] = idx++; }
void add(int a, int b, int c, int d, double e) { add(a, b, c, e), add(b, a, d, -e); }
double d[N];
int incf[N], q[N], st[N], pre[N];
bool SPFA() {
int hh = 0, tt = 0;
for (int i = 0; i <= n * 2 + 1; i++) d[i] = -2e9;
memset(incf, 0, sizeof incf), memset(st, 0, sizeof st);
memset(pre, -1, sizeof pre);
d[S] = 0, q[tt++] = S, st[S] = true, incf[S] = 2e9;
for (int t = q[hh]; hh++ != tt; t = q[hh]) {
if (hh == N - 1) hh = 0;
st[t] = false;
for (int i = h[t], v = val[i]; i != -1; i = ptr[i], v = val[i])
if (f[i] && d[v] < d[t] + w[i]) {
d[v] = d[t] + w[i], pre[v] = i;
incf[v] = min(incf[t], f[i]);
if (st[v]) continue;
q[tt++] = v, st[v] = true;
if (tt == N - 1) tt = 0;
}
}
return incf[T] > 0;
}
double EK() {
int res = 0;
double cost = 0;
while (SPFA()) {
int t = incf[T];
res += t, cost += t * d[T];
for (int i = T; i != S; i = val[pre[i] ^ 1])
f[pre[i]] -= t, f[pre[i] ^ 1] += t;
}
return cost;
}
bool check(double mid) {
// 连边
memset(h, -1, sizeof h), idx = 0;
// 源点到男生
for (int i = 1; i <= n; i++) add(S, i, 1, 0, 0);
// 女生到汇点
for (int i = 1; i <= n; i++) add(i + n, T, 1, 0, 0);
// 男女之间连边
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
add(i, j + n, 1, 0, a[i][j] - mid * b[i][j]);
// 费用流
return EK() >= 0;
}
int main() {
cin >> n;
S = 0, T = 2 * n + 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> a[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> b[i][j];
// 二分答案
double l = 0, r = 1e4;
while (r - l > eps) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
printf("%.6lf", r);
return 0;
}