zoukankan      html  css  js  c++  java
  • UVALive 7037:The Problem Needs 3D Arrays(最大密度子图)

    题目链接

    题意

    给出n个点,每个点有一个值,现在要选择一些点的集合,使得(选择的点生成的逆序对数目)/(选择的点的数量)的比率最大。

    思路

    点与点之间生成一个逆序对可以看做是得到一个边,那么就是分数规划问题|E|/|V|,即求最大密度子图。

    先处理出所有的逆序对,然后把这些逆序对看作边。

    二分枚举 h(g) = |E| - g * |V|中的g,h(g)为递减函数,把g看做点权,转化为最大权闭合图处理,当 h(g) 为0时,得到最优解,这时候的g就是答案。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> pii;
    const int INF = 0x3f3f3f3f;
    const int N = 11111;
    const double eps = 1e-8;
    const double inf = 1000000000;
    struct Edge {
    	int u, v, nxt;
    	double cap;
    } edge[N*4];
    int S, T, n, m, a[N], head[N], tot, pre[N], cur[N], gap[N], dis[N];
    pii p[N];
    
    void Add(int u, int v, double cap) {
    	edge[tot] = (Edge) { u, v, head[u], cap }; head[u] = tot++;
    	edge[tot] = (Edge) { v, u, head[v], 0 }; head[v] = tot++;
    }
    
    void BFS(int T) {
    	memset(dis, INF, sizeof(dis));
    	memset(gap, 0, sizeof(gap));
    	queue<int> que;
    	que.push(T); dis[T] = 0; gap[0] = 1;
    	while(!que.empty()) {
    		int u = que.front(); que.pop();
    		for(int i = head[u]; ~i; i = edge[i].nxt) {
    			int v = edge[i].v;
    			if(dis[v] != INF) continue;
    			dis[v] = dis[u] + 1;
    			gap[dis[v]]++;
    			que.push(v);
    		}
    	}
    }
    
    double ISAP(int S, int T, int n) {
    	BFS(T);
    	memcpy(cur, head, sizeof(cur));
    	int u = pre[S] = S, i, index;
    	double ans = 0, flow;
    	while(dis[S] < n) {
    		if(u == T) {
    			flow = inf; index = u;
    			for(u = S; u != T; u = edge[cur[u]].v)
    				if(flow > edge[cur[u]].cap) flow = edge[cur[u]].cap, index = u;
    			for(u = S; u != T; u = edge[cur[u]].v)
    				edge[cur[u]].cap -= flow, edge[cur[u]^1].cap += flow;
    			ans += flow; u = index;
    		}
    		for(i = cur[u]; ~i; i = edge[i].nxt)
    			if(dis[edge[i].v] == dis[u] - 1 && edge[i].cap > 0) break;
    		if(~i) {
    			cur[u] = i; pre[edge[i].v] = u; u = edge[i].v;
    		} else {
    			if(--gap[dis[u]] == 0) break;
    			int md = n + 1;
    			for(i = head[u]; ~i; i = edge[i].nxt)
    				if(dis[edge[i].v] < md && edge[i].cap > 0)
    					cur[u] = i, md = dis[edge[i].v];
    			gap[dis[u] = md + 1]++;
    			u = pre[u];
    		}
    	}
    	return ans;
    }
    
    void Build(double g) {
    	memset(head, -1, sizeof(head)); tot = 0;
    	for(int i = 1; i <= n; i++) Add(i, T, g);
    	for(int i = 1; i <= m; i++) {
    		Add(S, i + n, 1);
    		Add(i + n, p[i].first, inf);
    		Add(i + n, p[i].second, inf);
    	}
    }
    
    int main() {
    	int t; scanf("%d", &t);
    	for(int cas = 1; cas <= t; cas++) {
    		scanf("%d", &n);
    		for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    		m = 0;
    		for(int i = 1; i <= n; i++)
    			for(int j = i + 1; j <= n; j++)
    				if(a[i] > a[j]) p[++m] = {i, j};
    		double l = 0, r = m + 1, now;
    		S = 0, T = n + m + 1;
    		while(r - l >= eps) {
    			double mid = (l + r) / 2;
    			Build(mid);
    			now = 1.0 * m - ISAP(S, T, T + 1);
    			if(now < eps) r = mid;
    			else l = mid;
    		}
    		printf("Case #%d: %.12f
    ", cas, l);
    	}
    	return 0;
    }
    
    /*
    1
    5
    3 4 2 5 1
    
    Case #1: 1.250000000000
    */
    
  • 相关阅读:
    POJ 1003 解题报告
    POJ 1004 解题报告
    POJ-1002 解题报告
    vi--文本编辑常用快捷键之光标移动
    常用图表工具
    September 05th 2017 Week 36th Tuesday
    September 04th 2017 Week 36th Monday
    September 03rd 2017 Week 36th Sunday
    September 02nd 2017 Week 35th Saturday
    September 01st 2017 Week 35th Friday
  • 原文地址:https://www.cnblogs.com/fightfordream/p/7683838.html
Copyright © 2011-2022 走看看