Loj2003「SDOI2017」新生舞会

这道题太明显了,一眼看过去就知道是0/1分数规划。
先用二分枚举mid,假设mid就是c
我们要判断的是
a1+a2+...+anb1+b2+...+bn>=c\frac{a_1+a_2+...+a_n}{b_1+b2+...+b_n}>=c
然后我们就可以把分母乘到c上面去,得到
a1+a2+...+an>=c(b1+b2+...+bn) a_1+a_2+...+a_n>=c(b_1+b2+...+b_n)
这样以后化简完不就没得做了吗?
如果做过很多道0/1分数规划的题一定会知道用图论来求,那么我们就可以把每一项a和b放在一起,得到
(a1cb1)+(a2cb2)+...+(ancbn)>=0 (a_1-c*b_1)+(a_2-c*b_2)+...+(a_n-c*bn)>=0
然后我们把男生和女生之间都连一条边,这条边流量为1,费用为a[j][j]cb[i][j]a[j][j]-c*b[i][j],用最大费用最大流跑一遍,看看最大的费用是否大于0即可
参考代码

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
#define inf 0x3f3f3f3f
#define eps 1e-8
using namespace std;
const int N = 2e2 + 6, M = 2e5 + 6;
int tot, Head[N<<1], ver[M<<1], W[M<<1], cur[N<<1], Next[M<<1]; double Leng[M<<1];
int mark[N<<1], vis[N<<1], S, T, n;
double dis[N<<1], ans, a[N][N], b[N][N];
void add(int x, int y, int w, double d) {
	ver[++tot] = y, W[tot] = w, Leng[tot] = d, Next[tot] = Head[x]; Head[x] = tot;
	ver[++tot] = x, W[tot] = 0, Leng[tot] = -d, Next[tot] = Head[y]; Head[y] = tot;
}

bool SPFA() {
	for (int i = S; i <= T; i++) dis[i] = -inf;
	memset(vis, 0, sizeof(vis)); queue<int> q;
	q.push(S); dis[S] = 0;
	while (!q.empty()) {
		int u = q.front(); q.pop(); vis[u] = 0;
		for (int i = Head[u]; i; i = Next[i]) {
			int v = ver[i];
			if (W[i] && dis[v] < dis[u] + Leng[i]) {
				dis[v] = dis[u] + Leng[i];
				if (!vis[v]) q.push(v), vis[v] = 1;
			}
		}
	}
	return dis[T] != -inf;
}

int dfs(int x, int now) {
	mark[x] = 1;
	if (x == T) return now;
	int val = 0;
	for (int &i = cur[x], t; i; i = Next[i]) {
		int v = ver[i];
		if (!mark[v] && W[i] && dis[v] == dis[x] + Leng[i]) {
			t = dfs(v, min(now, W[i]));
			if (t > 0) {
				W[i] -= t, W[i ^ 1] += t;
				val += t, now -= t;
				ans += t * Leng[i];
				if (now == 0) break;
			}
		}
	}
	return val;
}

void ZKW() {
	ans = 0;
	while (SPFA()) {
		memcpy(cur, Head, sizeof(Head));
		mark[T] = 1;
		while (mark[T]) {
			memset(mark, 0, sizeof(mark));
			dfs(S, inf);
		}
	}
}

bool check(double x) {
	S = 0, T = 2 * n + 1; tot = 1;
	memset(Head, 0, sizeof(Head));
	for (int i = 1; i <= n; i++)
		add(S, i, 1, 0), add(i + n, T, 1, 0);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			add(i, j + n, 1, a[i][j] - x * b[i][j]);
	ZKW();
	return ans >= 0.0;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			scanf("%lf", &a[i][j]);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			scanf("%lf", &b[i][j]);
	double l = 0.0, r = 10006.0, mid, Ans;
	while (r - l > eps) {
		mid = (l + r) / 2.0;
		if (check(mid)) Ans = mid, l = mid + eps;
		else r = mid - eps;
	}
	printf("%.6lf\n", Ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章