UVa 7037 ACM/ICPC 2014 Xian(网络流+最大密度子图)
2017-09-13 21:59
501 查看
7037 - The Problem Needs 3D Arrays
Time limit: 5.000 secondsA permutation is a sequence of integers p1, p2, ..., pn, consisting of n distinct positive integers and each of them does not exceed n. Assume that r(S) of sequence S denotes the number of inversions in sequence S (if i < j and Si > Sj, then the pair of
(i,j) is called an inversion of S), l(S) of sequence S denotes the length of sequence S. Given a permutation P of length n, it’s your task to find a subsequence S of P with maximum r(S) l(S) . A subsequence of P is a sequence (pi1,pi2,...,pit) which satisfies
that 0 < i1 < i2 < ... < it ≤ n.
Input
The first line of the input gives the number of test cases, T. T test cases follow. For each test case, the first line contains an integer n (1 ≤ n ≤ 100), the length of the permutationP . The second line contains n integers p1, p2, ..., pn, which represents
the permutation P.
Output
For each test case, output one line containing ‘Case #x: y’, where x is the test case number (starting from 1) and y is the maximum r(S) l(S) . Your answer will be considered correct if it is within an absolute error of 10−6 of the correct answer.
Sample Input
1
5
3 4 2 5 1
Sample Output
Case #1: 1.250000000000
大致题意:给你一个1~n的排列,然后让你在这个排列中挑选任意个数字,使得这些数字组成的子串的逆序对数量除以总长最大。
首先,我们先来回顾一下逆序对怎么计算。正常来说,O(N^2),但是大大可以用一些数据结构来优化。无非就是,加入一个数字的时候统计在此之前比该数字大的数字有多少个,然后再把这个数字加入。这种单点修改,区间查询,用树状数组再好不过了。
但是对于这题来说,由于数字可以是任意取,即不一定是取连续的一段,所以在子串的选取上就不太好处理。如果说本题是要求子串必须是原排列的连续一段,那么直接枚举长度和其实点,用上树状数组求逆序对,O(N^2logN)即可解决问题。但是这题不是这样的,后来又尝试着用搜索加剪枝去构造子串,但是无奈还是TLE。
后来看了看其他人的解法,网络流……的确,对于逆序对(ai,aj),我们连边i->j,构成一个图。那么,求子串逆序对与长度之比比最大,相当于在这个图中求一个子图,使得图的密度最大,即边数除以点数最大。于是,问题就转换成了求最大密度子图的经典问题。
说到最大密度子图,还是不得不提胡伯涛大神的那篇奇文,这个我已经引用第三次了,还是贴上来:胡伯涛《最小割模型在信息学竞赛中的应用》○| ̄|_……在这里面,大神提供了两种方法。但是不管怎么样,这题首先要建立在01分数规划问题的基础上,这个最近的是在多校赛中出现过。按照通用方法,求r(S)/l(S),我设最大值为g,那么有r(s)=l(s)*g,移项有r(s)-g*l(s)=0。然后二分g,判定结果与0的大小关系即可。但是知道了这个,我们还是需要知道怎么判定结果。这里,我们就说说两种不同的方式。
第一种方式,我们把边也抽象为点,然后赋值上点权1,代表边数。然后原本的点保留,并赋值上点权g,对应判定式中的g*r(s)。然后,对于存在于原图中的第i条边<u,v>,我们连边<i,u>和<i,v>。代表先决条件,要取边i,那么一定要对应取点u、v。聪明的读者或许已经发现,我们要求r(s)-g*l(s)最大,可以相当于是找一个最大权闭合子图,有正负点权,有先决关系,正好符合最大权闭合子图的模型,用最小割计算即可。
但是,我们发现,如果边比较多的时候,相应点也变多,貌似复杂度不太优,于是有了第二种方式。对于一个选定的点的集合,显然,我们选边一定要选所有的,以这个集合中点为端点的点。那么如何快速计算呢?我们考虑度的关系,如果把集合内所有的点关联的边分类,我们可以分成两类,一个是内向边,一个是外向边,图中红色为外向边,
黑色加粗的为内向边。我们发现,内向边的条数可以通过度和外向边的关系求出,而外向边又可以看成集合与集合外点之间的最小割。通过以下一系列的转换,我们同样可以把这个问题转换为最小割问题。具体来说就是,首先对于所有点,源点向它连一条边,容量为U,它连向汇点,容量为U-d[i]+2*g,然后对于边<u,v>我们连u->v和v->u容量为1。其中U表示一个足够大的容量(取边的数目即可),d[i]表示i点的度。具体的证明过程见大神的文章吧,符号不好打,然后也不是很好解释。
第二种方法,更加的复杂难懂,但是付出总是有回报,第二种方法效率非常高,一般来说比第一种方法快一倍,如果是稠密图,那么甚至可以快更多。具体见代码:
#include<bits/stdc++.h> #define INF 0x3f3f3f3f #define DB double #define eps 1e-7 #define M 100010 #define N 110 using namespace std; int n,m,a ,d ; vector<int> g ; DB ans; namespace ISAP { int H ,d ,cur ,pre ,gap ,Q[M]; struct Edge{int u,v,n;DB c;} E[M]; int nv,head,tail,cntE;DB flow,f; void init(){cntE=0; memset(H,-1,sizeof(H));} void addedge(int u,int v,DB c) { E[cntE]=Edge{u,v,H[u],c}; H[u]=cntE++; E[cntE]=Edge{v,u,H[v],0}; H[v]=cntE++; } void revbfs(int s,int t) { head=tail=0 ; memset(d,-1,sizeof(d)); memset(gap,0,sizeof(gap)); Q[tail++]=t;d[t]=0;gap[d[t]]=1; while (head!=tail) { int u=Q[head++]; for (int i=H[u];~i;i=E[i].n) { int v=E[i].v; if (~d[v]) continue; d[v]=d[u]+1; gap[d[v]]++; Q[tail++]=v; } } } DB isap(int s,int t) { memcpy(cur,H,sizeof(cur)); nv=t; flow=0; revbfs(s,t); int u=pre[s]=s,i; while (d[s]<nv) { if (u==t) { f=INF; for (i=s;i!=t;i=E[cur[i]].v) if (f-E[cur[i]].c>=eps) f=E[cur[i]].c,u=i; flow += f; for (i=s;i!=t;i=E[cur[i]].v) E[cur[i]].c-=f,E[cur[i]^1].c+=f; } for (i=cur[u];~i;i=E[i].n) if (E[i].c>=eps&&d[u]==d[E[i].v]+1) break ; if (~i) cur[u]=i,pre[E[i].v]=u,u=E[i].v; else { if (0==--gap[d[u]]) break ; int minv=nv,v; for (int i=H[u];~i;i=E[i].n) { v=E[i].v; if (E[i].c>=eps&&minv>d[v]) minv=d[v],cur[u]=i; } d[u]=minv+1; gap[d[u]]++; u=pre[u]; } } return flow; } } bool check(DB x) { ISAP::init(); int t=n+1,s=0; for(int i=1;i<=n;i++) { for(int j=0;j<g[i].size();j++) { ISAP::addedge(i,g[i][j],1); ISAP::addedge(g[i][j],i,1); } ISAP::addedge(s,i,m); ISAP::addedge(i,t,m+2*x-d[i]); } return n*m-ISAP::isap(s,t)>=eps; } int main() { int T_T,T; cin>>T_T;T=T_T; while(T_T--) { ans=0; m=0; scanf("%d",&n); memset(g,0,sizeof(g)); memset(d,0,sizeof(d)); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if (a[i]>a[j]) { ++m;d[i]++;d[j]++; g[i].push_back(j); } DB l=0,r=(n+1)/2.0,mid; while(r-l>eps) { mid=(r+l)/2.0; if (check(mid)) ans=mid,l=mid; else r=mid; } printf("Case #%d: %.6f\n",T-T_T,ans); } return 0; }
相关文章推荐
- 2014-2015 ACM-ICPC, Asia Xian Regional Contest C – The Problem Needs 3D Arrays(最大密度子图)
- UVAlive 7037 - The Problem Needs 3D Arrays(网络流‘最大密度子图)
- [最大密度子图 最小割] ACM 2014 Xian C The Problem Needs 3D Arrays
- UVALive 7037 (最大密度子图 网络流)
- UVALive 7037 The Problem Needs 3D Arrays(最大密度子图)
- UvaLive 7037 The Problem Needs 3D Arrays 【最大密度子图-最大权闭合子图做法】
- Uvalive 7037 The Problem Needs 3D Arrays(最大密度子图)
- The 2014 ACMICPC Asia Regional Xian
- 【POJ3155】【网络流】【最大密度子图】Hard Life 题解
- poj 3155 Hard Life 网络流——最大密度子图
- UVALive Problem 7073 Song Jiang's rank list(排序)——2014ACM/ICPC亚洲区广州站
- The 2014 ACMICPC Asia Regional Xian Online
- 2014-2015 ACM-ICPC, Asia Xian Regional Contest GThe Problem to Slow Down You
- Codeforces GYM 100548 K - Last Defence 2014-2015 ACM-ICPC, Asia Xian Regional Contest
- Codeforces GYM 100548 F - Color 2014-2015 ACM-ICPC, Asia Xian Regional Contest
- 2014-2015 ACM-ICPC, Asia Xian Regional Contest C
- 2014-2015 ACM-ICPC, Asia Xian Regional Contest G
- 2014-2015 ACM-ICPC, Asia Xian Regional Contest F Color
- 2014-2015 ACM-ICPC, Asia Xian Regional Contest G The Problem to Slow Down You 回文树
- UVA 7146 Defeat The Enemy 2014 ACM-ICPC Asia shanghai Regional Contest