zoukankan      html  css  js  c++  java
  • 【SDOI2017】新生舞会

    题面

    题解

    一眼(0/1)分数规划

    二分答案(mid),我们要(sumlimits_i a^{'}_i - midsumlimits_i b_i^{'})最大

    那么我们将(a_{i,j}-mid imes b_{i,j})作为((i,j))的边权

    跑一遍二分图最大权匹配即可。

    代码

    // luogu-judger-enable-o2
    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #define RG register
    #define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
    #define clear(x, y) memset(x, y, sizeof(x))
    
    inline int read()
    {
    	int data = 0, w = 1; char ch = getchar();
    	while(ch != '-' && (!isdigit(ch))) ch = getchar();
    	if(ch == '-') w = -1, ch = getchar();
    	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
    	return data * w;
    }
    
    const int N(110), maxn(5e5 + 10);
    const double eps(1e-8);
    
    struct edge { int next, to, cap; double dis; } e[maxn];
    int head[maxn], e_num = -1, S, T, a[N][N], b[N][N];
    int pre[maxn], pre_e[maxn], vis[maxn], n;
    double dis[maxn], cost;
    
    inline void add_edge(int from, int to, int cap, double dis)
    {
    	e[++e_num] = (edge) {head[from], to, cap, dis}; head[from] = e_num;
    	e[++e_num] = (edge) {head[to], from,  0, -dis}; head[to]   = e_num;
    }
    
    std::queue<int> q;
    void MinCostMaxFlow()
    {
    	cost = 0;
    	while(1)
    	{
    		std::fill(dis + S, dis + T + 1, -1e20); clear(vis, 0);
    		vis[S] = 1, dis[S] = 0, q.push(S);
    		while(!q.empty())
    		{
    			int x = q.front(); q.pop();
    			for(RG int i = head[x]; ~i; i = e[i].next)
    			{
    				int to = e[i].to; double ds = e[i].dis + dis[x];
    				if(e[i].cap > 0 && dis[to] < ds)
    				{
    					dis[to] = ds, pre[to] = x, pre_e[to] = i;
    					if(!vis[to]) vis[to] = 1, q.push(to);
    				}
    			}
    			vis[x] = 0;
    		}
    
    		if(dis[T] == -1e20) return;
    		int cap = 1e9;
    		for(RG int i = T; i ^ S; i = pre[i])
    			cap = std::min(cap, e[pre_e[i]].cap);
    		cost += 1. * cap * dis[T];
    		for(RG int i = T; i ^ S; i = pre[i])
    			e[pre_e[i]].cap -= cap, e[pre_e[i] ^ 1].cap += cap;
    	}
    }
    
    bool check(double mid)
    {
    	e_num = -1, S = 1, T = (n << 1) + 2;
    	for(RG int i = S; i <= T; i++) head[i] = -1;
    	for(RG int i = 1; i <= n; i++) add_edge(S, i + 1, 1, 0);
    	for(RG int i = n + 2; i < T; i++) add_edge(i, T, 1, 0);
    	for(RG int i = 1; i <= n; i++) for(RG int j = 1; j <= n; j++)
    		add_edge(i + 1, j + n + 1, 1, 1. * a[i][j] - 1. * b[i][j] * mid);
    	MinCostMaxFlow(); return fabs(cost) <= eps || cost > eps;
    }
    
    int main()
    {
    	n = read();
    	for(RG int i = 1; i <= n; i++)
    		for(RG int j = 1; j <= n; j++)
    			a[i][j] = read();
    	for(RG int i = 1; i <= n; i++)
    		for(RG int j = 1; j <= n; j++)
    			b[i][j] = read();
    	double l = -1e7, r = 1e7;
    	while(fabs(r - l) > eps)
    	{
    		double mid = (l + r) / 2;
    		if(check(mid)) l = mid;
    		else r = mid;
    	}
    	printf("%.6lf
    ", r);
    	return 0;
    }
    
  • 相关阅读:
    《C》指针
    《C》变量
    《C》数组
    《C》VS控制台应用
    listagg wm_concat 行转列
    Linux学习之shell script
    Linux学习之正则表达式sed
    Linux学习之正则表达式grep
    Linux学习之SAMBA共享(密码验证)
    Linux学习之SAMBA共享(无密码)
  • 原文地址:https://www.cnblogs.com/cj-xxz/p/10269336.html
Copyright © 2011-2022 走看看