zoukankan      html  css  js  c++  java
  • 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%的数据,(1le nle 5)

    对于40%的数据,(1le nle 18)

    另有20%的数据,(b_{i,j}le 1)

    对于100%的数据,(1le nle 100,1le 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
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    HTML学习基础(一)
    初识C#
    SQL小知识
    jQuery(四)
    jQuery(三)
    认识jQuery(二)
    认识Jquery
    初识Javascript
    初识HTML
    博客,你好!
  • 原文地址:https://www.cnblogs.com/olinr/p/10542136.html
Copyright © 2011-2022 走看看