Loading

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\n", cas, l);
	}
	return 0;
}

/*
1
5
3 4 2 5 1

Case #1: 1.250000000000
*/
posted @ 2017-10-17 20:49  Shadowdsp  阅读(519)  评论(0编辑  收藏  举报