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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章