P3705 [SDOI2017]新生舞会 01分数规划+费用流

$ \color{#0066ff}{ 题目描述 }$

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

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

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

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

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

Cathy想先用一个程序通过\(a_{i,j}\)\(b_{i,j}\),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值最大。

\(\color{#0066ff}{输入格式}\)

第一行一个整数n。

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

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

\(\color{#0066ff}{输出格式}\)

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

\(\color{#0066ff}{输入样例}\)

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

\(\color{#0066ff}{输出样例}\)

5.357143

\(\color{#0066ff}{数据范围与提示}\)

对于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\)

\(\color{#0066ff}{题解}\)

很显然这是个01分数规划, 二分答案就行

考虑二分完之后咋整,要是新权值和最小(大), 二人一一对应, 用费用流就行了

注意二分范围
#include<bits/stdc++.h>
#define LL long long
LL in() {
	char ch; LL x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}
const double eps = 1e-8;
const int maxn = 1e5 + 100;
const int maxm = 1020;
const int inf = 0x7fffffff;
int n, s, t;
struct node {
	int to, can;
	double dis;
	node *nxt, *rev; 
	node(int to = 0, int can = 0, double dis = 0, node *nxt = NULL): to(to), can(can), dis(dis), nxt(nxt) { rev = NULL; }
}pool[maxn], *tail;
node *head[maxm];
bool vis[maxm];
double dis[maxm];
int a[maxm][maxm], b[maxm][maxm];
void init() {
	for(int i = s; i <= t; i++) head[i] = NULL;
	tail = pool;
}
void add(int from, int to, int can, double dis) {
	head[from] = new(tail++) node(to, can, dis, head[from]);
}
void link(int from, int to, int can, double dis) {
	add(from, to, can, dis), add(to, from, 0, -dis);
	(head[from]->rev = head[to])->rev = head[from];
}
bool spfa() {
	for(int i = s; i <= t; i++) vis[i] = false, dis[i] = 1e15;
	std::deque<int> q;
	dis[t] = 0;
	q.push_back(t);
	while(!q.empty()) {
		int tp = q.front(); q.pop_front();
		vis[tp] = false;
		for(node *i = head[tp]; i; i = i->nxt) {
			if(dis[i->to] - (dis[tp] - i->dis) > eps && i->rev->can) {
				dis[i->to] = dis[tp] - i->dis;
				if(!vis[i->to]) {
					vis[i->to] = true;
					if(!q.empty() && dis[q.front()] - dis[i->to] > eps) q.push_front(i->to);
					else q.push_back(i->to);
				}
			}
		}
	}
	return 1e15 - dis[s] > eps;
}
int dfs(int x, int change) {
	if(x == t || !change) return change;
	vis[x] = true;
	int flow = 0, ls;
	for(node *i = head[x]; i; i = i->nxt) {
		if(!vis[i->to] && fabs(dis[i->to] - (dis[x] - i->dis)) <= eps && (ls = dfs(i->to, std::min(change, i->can)))) {
			change -= ls;
			flow += ls;
			i->rev->can += ls;
			i->can -= ls;
			if(!change) break;
		}
	}
	return flow;
}
double zkw() {
	double cost = 0;
	while(spfa()) {
		vis[t] = true;
		while(vis[t]) {
			for(int i = s; i <= t; i++) vis[i] = false;
			cost += 1.0 * dfs(s, inf) * dis[s];
		}
	}
	return cost;
}
bool ok(double mid) {
	s = 0, t = 2 * n + 1;
	init();
	for(int i = 1; i <= n; i++) link(s, i, 1, 0), link(i + n, t, 1, 0);
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++) 
			link(i, j + n, 1, (double)(mid * b[i][j] - 1.0 * a[i][j]));
	return zkw() <= -eps;
}

int main() {
	n = in();
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++) 
			a[i][j] = in();
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++) 
			b[i][j] = in();
	double l = 0, r = 1e5, ans = 0;
	while(r - l >= eps) {
		double mid = (l + r) / 2.0;
		if(ok(mid)) ans = mid, l = mid;
		else r = mid;
	}
	printf("%.6f\n", ans);
	return 0;
}
posted @ 2019-03-16 14:08  olinr  阅读(119)  评论(0编辑  收藏  举报