题目:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=94761#problem/C
题意:
序列T,r(S)表示子序列S(不连续)中的逆序数对,L(S) 表示S 的长度,求出 r(S) / L(S) 的最大值
思路:
r(S) 可以看成边,L(S) 可以看成点,使得 E / V 最大,则题目转化为 求解 最大密度子图。
存在逆序数对(u,v),二分答案g, 建边:
1. s 与 边 id 建一条容量的1 的边。
2. id与u点 建一条容量为inf 的边,id与v点建一条容量为inf的边。
3. u 与t 建一条容量为g 的边。
使得 res = 逆序数对总数 - maxflow 趋于0,则此时g 为答案。
AC.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const int maxn = 7000;
const int maxm = 1e7;
int n;
int val[maxn];
int gap[maxn], dep[maxn], cur[maxn];
int tot, head[maxn];
struct Edge{
int to, next;
double cap, flow;
}edge[maxm];
void init()
{
tot = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v, double w)
{
edge[tot].to = v; edge[tot].cap = w; edge[tot].flow = 0.0;
edge[tot].next = head[u]; head[u] = tot++;
edge[tot].to = u; edge[tot].cap = 0.0; edge[tot].flow = 0.0;
edge[tot].next = head[v]; head[v] = tot++;
}
int Q[maxn];
void bfs(int start, int endd)
{
memset(dep, -1, sizeof(dep));
memset(gap, 0, sizeof(gap));
gap[0] = 1;
int fron = 0, rear = 0;
dep[endd] = 0;
Q[rear++] = endd;
while(fron != rear) {
int u = Q[fron++];
for(int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if(dep[v] != -1) continue;
Q[rear++] = v;
dep[v] = dep[u] + 1;
gap[dep[v]]++;
}
}
}
int S[maxn];
double sap(int start, int endd, int N)
{
bfs(start, endd);
memcpy(cur, head, sizeof(head));
int top = 0;
int u = start;
double ans = 0;
while(dep[start] < N) {
if(u == endd) {
double Min = inf;
int inser;
for(int i = 0; i < top; ++i) {
if(Min > edge[S[i]].cap -edge[S[i]].flow) {
Min = edge[S[i]].cap - edge[S[i]].flow;
inser = i;
}
}
for(int i = 0; i < top; ++i) {
edge[S[i]].flow += Min;
edge[S[i]^1].flow -= Min;
}
ans += Min;
top = inser;
u = edge[S[top]^1].to;
continue;
}
bool flag = 0;
int v;
for(int i = cur[u]; ~i; i = edge[i].next) {
v = edge[i].to;
if(edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]) {
flag = true;
cur[u] = i;
break;
}
}
if(flag) {
S[top++] = cur[u];
u = v;
continue;
}
int Min = N;
for(int i = head[u]; ~i; i = edge[i].next) {
if(edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) {
Min = dep[edge[i].to];
cur[u] = i;
}
}
gap[dep[u]]--;
if(!gap[dep[u]]) return ans;
dep[u] = Min + 1;
gap[dep[u]]++;
if(u != start) u = edge[S[--top]^1].to;
}
return ans;
}
//int du[maxn];
//void build(int s, int t, int sum, double g)
//{
// init();
// for(int i = 1; i <= n; ++i) {
// for(int j = i+1; j <= n; ++j) {
// addedge(i, j, 1.0);
//
// // addedge(j, i, 1.0);
// }
// }
//
// for(int i = 1; i <= n; ++i) {
// addedge(s, i, sum);
// addedge(i, t, sum*1.0+2*g-du[i]*1.0);
// }
//}
struct node{
int u, v;
}g[maxm];
void build(int s, int t, double mid, int sum)
{
init();
for(int i = 1; i <= sum; ++i) {
addedge(s, i, 1);
addedge(i, g[i].u+sum, inf);
addedge(i, g[i].v+sum, inf);
}
for(int i = 1; i <= n; ++i) {
addedge(i+sum, t, mid);
}
}
int main()
{
//freopen("in", "r", stdin);
int T, ca = 1;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &val[i]);
}
int sum = 1;
for(int i = 1; i <= n; ++i) {
for(int j = i+1; j <= n; ++j) {
if(val[i] > val[j]) {
g[sum].u = i;
g[sum++].v = j;
}
}
}
sum--;
int s = 0, t = sum + n + 1;
double l = 0, r = sum;
double ans = 0;
while((r-l) > eps) {
double mid = (l+r) / 2.0;
build(s, t, mid, sum);
double res = 1.0*sum - sap(s, t, t+1);
if(res >= eps) {
ans = mid;
l = mid;
}
else r = mid;
}
printf("Case #%d: %.12lf\n", ca++, ans);
}
return 0;
}
第二种建图:
原图拆成两条有向边容量为1,源点到每一点建一条容量为sum的边,每一点到汇点建边容量为sum + 2*mid - du[v], du[v] 为v点的度。
二分答案,当 sum*n - sap(s,t) 趋近于0, 得到答案。
AC.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const int maxn = 7000;
const int maxm = 1e7;
int n;
int val[maxn];
int gap[maxn], dep[maxn], cur[maxn];
int tot, head[maxn];
struct Edge{
int to, next;
double cap, flow;
}edge[maxm];
void init()
{
tot = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v, double w)
int Q[maxn];
void bfs(int start, int endd)
int S[maxn];
double sap(int start, int endd, int N)
int du[maxn];
struct node{
int u, v;
}g[maxn];
void build(int s, int t, int sum, double mid)
{
init();
for(int i = 1; i <= sum; ++i) {
addedge(g[i].u, g[i].v, 1);
addedge(g[i].v, g[i].u, 1);
}
for(int i = 1; i <= n; ++i) {
addedge(s, i, sum);
addedge(i, t, sum*1.0+2*mid-du[i]*1.0);
}
}
int main()
{
//freopen("in", "r", stdin);
int T, ca = 1;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &val[i]);
}
memset(du, 0, sizeof(du));
int sum = 1;
for(int i = 1; i <= n; ++i) {
for(int j = i+1; j <= n; ++j) {
if(val[i] > val[j]) {
g[sum].u = i;
g[sum++].v = j;
du[i]++;
du[j]++;
}
}
}
sum--;
int s = 0, t = n + 1;
double l = 0, r = sum;
double ans = 0;
while((r-l) > eps) {
double mid = (l+r) / 2.0;
build(s, t, sum, mid);
double tmp = sap(s, t, t+1);
double res = 1.0*sum*n - tmp;
if(res >= eps) {
ans = mid;
l = mid;
}
else r = mid;
}
printf("Case #%d: %.12lf\n", ca++, ans);
}
return 0;
}