luogu P3705 [SDOI2017]新生舞会

https://www.luogu.com.cn/problem/P3705
显然是01分数规划
于是乎
∑ a i − C ∗ b i = 0 \sum a_i-C*b_i=0 aiCbi=0
二分C然后跑最大费用最大流即可
code

#include<bits/stdc++.h>
#define N 25555
using namespace std;
const double INF = 1e9;
const double eps = 1e-9;
struct edge {
	int v, nxt, c;
	double w;
} e[N << 1];
int p[N], eid;
void init() {
	memset(p, -1, sizeof p);
	eid = 0;
}
void insert(int u, int v, int c, double w) {
	e[eid].v = v;
	e[eid].c = c;
	e[eid].w = w;
	e[eid].nxt = p[u];
	p[u] = eid ++;
}
void add(int u, int v, int c, double w) { //printf("%d --> %d   %d %lf\n", u, v, c, w);
	insert(u, v, c, w);
	insert(v, u, 0, -w);
}
queue<int> q;
int n, m, S, T, pre[N], vis[N];
double dis[N];
int spfa() {
	for(int i = 0; i <= T; i ++) dis[i] = -INF, pre[i] = -1, vis[i] = 0;
	dis[S] = 0; q.push(S);
	while(q.size()) {
		int u = q.front(); q.pop();
		vis[u] = 0;
		for(int i = p[u]; i + 1; i = e[i].nxt) {
			int v = e[i].v; 
			if(e[i].c && dis[u] + e[i].w > dis[v]) {
				dis[v] = dis[u] + e[i].w;
				pre[v] = i;
				if(!vis[v]) vis[v] = 1, q.push(v);
			}
		}
	}
	//for(int i = 0; i <= T; i ++) printf("%d ", pre[i]); printf("\n");
	return pre[T] != -1;
}
double mcfc() {
	double ret = 0;
	for(; spfa(); ) {
		int flow = INF;
		for(int i = T; i != S; i = e[pre[i] ^ 1].v) {
			flow = min(flow, e[pre[i]].c);
		}
		for(int i = T; i != S; i = e[pre[i] ^ 1].v) {
			e[pre[i]].c -= flow, e[pre[i] ^ 1].c += flow;
			ret += (double)flow * e[pre[i]].w;
		}
	//	printf("  %d   %lf\n", flow, ret);
	}
	return ret;
}
int a[205][205], b[205][205];
int check(double x) {
	init(); S = 0, T = 2 * n + 1;
	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, (double)a[i][j] - x * b[i][j]);
	return mcfc() >= 0;			
}
int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= n; j ++)
			scanf("%d", &a[i][j]);
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= n; j ++)
			scanf("%d", &b[i][j]);
	
	double l = 0, r = 1e5;
	while(l + eps < r) {
		double mid = (l + r) / 2.0;
		if(check(mid)) l = mid;
		else r = mid;
	}
	printf("%.6lf", l);
	return 0;
}
posted @ 2021-07-11 22:34  lahlah  阅读(28)  评论(0编辑  收藏  举报