散落星河的记忆🌠
Published on 2017-09-02 11:31 in 暂未分类 with 散落星河的记忆🌠

[BZOJ 4819] [SDOI 2017] 新生舞会

Description

学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴。

\(n\) 个男生和 \(n\) 个女生参加舞会买一个男生和一个女生一起跳舞,互为舞伴。

Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出 \(a_{i,j}\)

Cathy还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出 \(b_{i,j}\),表示第 \(i\) 个男生和第 \(j\) 个女生一起跳舞时的不协调程度。

当然,还需要考虑很多其他问题。

Cathy想先用一个程序通过 \(a_{i,j}\)\(b_{i,j}\) 求出一种方案,再手动对方案进行微调。

Cathy找到你,希望你帮她写那个程序。

一个方案中有n对舞伴,假设没对舞伴的喜悦程度分别是 \(a'_1,a'_2,...,a'_n\),假设每对舞伴的不协调程度分别是 \(b'_1,b'_2,...,b'_n\)。令

\[C=\frac{a'_1+a'_2+...+a'_n}{b'_1+b'_2+...+b'_n} \]

Cathy希望 \(C\) 值最大。

Input

第一行一个整数 \(n\)

接下来 \(n\) 行,每行 \(n\) 个整数,第 \(i\) 行第 \(j\) 个数表示 \(a_{i,j}\)

接下来 \(n\) 行,每行 \(n\) 个整数,第 \(i\) 行第 \(j\) 个数表示 \(b_{i,j}\)

Output

一行一个数,表示 \(C\) 的最大值。四舍五入保留 \(6\) 位小数,选手输出的小数需要与标准输出相等。

Sample Input

3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9

Sample Output

5.357143

HINT

对于10%的数据,\(1\le n\le 5\)
对于40%的数据,\(1\le n\le 18\)
另有20%的数据,\(b_{i,j}\le 1\)
对于100%的数据,\(1\le n\le 100,1\le a_{i,j},b_{i,j}<=10^4\)

Solution

分数规划,二分答案 \(mid\),将边的费用设为 \(b_{i,j}\cdot mid-a_{i,j}\),若最小费用 \(>0\),则令 \(r=mid\),否则令 \(l=mid\)

注意 \(1e9+1e^{-8}\) 会爆 \(double\) 的精度,所以避免写 \(\inf+eps\)

Code

#include <queue>
#include <cstdio>
#include <algorithm>

const double eps = 1e-8, INF = 1e8;
int n, a[102][102], b[102][102], head[205], tot = 1, S, T, p[205], c[205]; double d[205];

struct Edge {
	int u, v, nxt, f, c; double w; Edge(){}
	Edge(int u, int v, int nxt, int f, int c, double w) : u(u), v(v), nxt(nxt), f(f), c(c), w(w) {}
} e[20402];

int read() {
	int x = 0; char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
	return x;
}
void adde(int u, int v) {
	e[++tot] = Edge(u, v, head[u], 0, 1, 0), head[u] = tot;
	e[++tot] = Edge(v, u, head[v], 0, 0, 0), head[v] = tot;
}
bool spfa(int &flow, double &cost) {
	int inq[205] = {}, u; std::queue<int> q;
	for (int i = 1; i <= T; ++i) d[i] = INF;
	d[S] = p[S] = 0, q.push(S), inq[S] = 1, c[S] = INF;
	while (!q.empty()) {
		u = q.front(), q.pop(), inq[u] = 0;
		for (int i = head[u]; i; i = e[i].nxt)
			if (e[i].c > e[i].f && d[e[i].v] > d[u] + e[i].w + eps) {
				d[e[i].v] = d[u] + e[i].w, p[e[i].v] = i, c[e[i].v] = std::min(c[u], e[i].c - e[i].f);
				if (!inq[e[i].v]) q.push(e[i].v), inq[e[i].v] = 1;
			}
	}
	if (d[T] + 1 > INF) return false;
	flow += c[T], cost += c[T] * d[T], u = T;
	while (u != S) e[p[u]].f += c[T], e[p[u] ^ 1].f -= c[T], u = e[p[u]].u;
	return true;
}
double mcmf() {
	int flow = 0; double cost = 0;
	while (spfa(flow, cost)) {}
	return cost;
}
int main() {
	n = read(), S = n << 1 | 1, T = S + 1;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j) a[i][j] = read();
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j) b[i][j] = read();
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j) adde(i, n + j);
	for (int i = 1; i <= n; ++i) adde(S, i), adde(n + i, T);
	double l = 0, r = 10000, mid;
	while (l + eps < r) {
		mid = (l + r) / 2;
		for (int i = 1, k = 0; i <= n; ++i)
			for (int j = 1; j <= n; ++j) k += 2, e[k].w = mid * b[i][j] - a[i][j], e[k ^ 1].w = a[i][j] - mid * b[i][j];
		for (int i = 2; i <= tot; ++i) e[i].f = 0;
		if (mcmf() > eps) r = mid; else l = mid;
	}
	printf("%.6lf", l);
	return 0;
}
posted @ 2019-02-20 19:38  散落星河的记忆🌠  阅读(130)  评论(0编辑  收藏  举报