bzoj4819 [Sdoi2017]新生舞会 分数规划+最大费用最大流

题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4819

题解

首先上面说,

\[C = \frac{\sum\limits_{i=1}^n a'_i}{\sum\limits_{i=1}^n b'_i} \]

\(C\) 最大。这是一种非常常见的分数规划模型,于是二分后转化成

\[\sum\limits_{i=1}^n a'_i - k \cdot b'_i \geq 0 \]

于是问题转化为求出权值和最大的方案满足每个人只选择一次。

可以是一个二分图匹配的模型,所以可以转化为最大费用最大流完成。


时间复杂度很玄学,不知道为什么能过。

调了半年的最小费用最大流突然想起来应该维护最大费用最大流。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int NN = 100 + 7;
const int N = 100 * 100 + 7;
const int M = 100 * 100 + 200 + 7;
const int INF = 0x3f3f3f3f;

int n, nod, S, T, hd, tl;
int a[NN][NN], b[NN][NN], id[NN][NN], q[N], inq[N], cur[N], vis[N];
double dis[N];

struct Edge { int to, ne, f; double w; } g[M << 1]; int head[N], tot = 1;
inline void addedge(int x, int y, int z, double w) { g[++tot].to = y, g[tot].f = z, g[tot].w = w, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y, int f, double w) { addedge(x, y, f, w), addedge(y, x, 0, -w); }

inline void qpush(int x) { ++tl; tl == N ? tl = 1 : 0; q[tl] = x; }
inline int qhead() { ++hd; hd == N ? hd = 1 : 0; return q[hd]; }
inline bool spfa() {
	for (int i = 1; i <= nod; ++i) dis[i] = -1e9, vis[i] = 0, cur[i] = head[i];
	q[hd = 0, tl = 1] = S, inq[S] = 0, dis[S] = 0;
	while (hd != tl) {
		int x = qhead();
		inq[x] = 0;
		for fec(i, x, y) if (g[i].f && smax(dis[y], dis[x] + g[i].w) && !inq[y]) qpush(y), inq[y] = 1;
	}
	return dis[T] > -1e9;
}
inline int dfs(int x, int a, double &mc) {
	if (x == T || !a) return mc += dis[x] * a, a;
	int f, flow = 0;
	vis[x] = 1;
	for (int &i = cur[x]; i; i = g[i].ne) {
		int y = g[i].to
		if (vis[y] || (dis[y] != dis[x] + g[i].w)) continue;
		if (!(f = dfs(y, std::min(g[i].f, a), mc))) continue;
		g[i].f -= f, g[i ^ 1].f += f;
		flow += f, a -= f;
		if (!a) return flow;
	}
	dis[x] = INF;
	return flow;
}
inline double mcmf() {
	double ans = 0, tmp = 0;
	while (spfa()) tmp = 0, dfs(S, INF, tmp), ans += tmp;
	return ans;
}

inline bool check(double mid) {
	memset(head, 0, sizeof(int) * (nod + 1)), tot = 1;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j) adde(i, j + n, 1, a[i][j] - mid * b[i][j]);
	for (int i = 1; i <= n; ++i) adde(S, i, 1, 0), adde(i + n, T, 1, 0);
	return mcmf() >= 0;
}

inline void work() {
	S = ++nod, T = ++nod;
	double l = 0, r = 1e4 + 7;
	while (r - l  >= 1e-7) {
		double mid = (l + r) / 2;
		if (check(mid)) l = mid;
		else r = mid;
	}
	printf("%.6lf\n", l);
}

inline void init() {
	read(n);
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j) read(a[i][j]), id[i][j] = ++nod;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j) read(b[i][j]);
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	init();
	work();
	fclose(stdin), fclose(stdout);
	return 0;
}
posted @ 2019-10-29 11:01  hankeke303  阅读(140)  评论(0编辑  收藏  举报