您的位置:首页 > 其它

codevs1906 最长递增子序列问题

2016-08-06 15:56 337 查看
题目描述 Description

给定正整数序列x1,..... , xn 。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。



输入描述
Input Description


第1 行有1个正整数n,表示给定序列的长度。接
下来的1 行有n个正整数x1.....xn 。



输出描述
Output Description


第1 行是最长递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。



样例输入
Sample Input


4
3 6 2 5



样例输出
Sample Output


2
2
3

正解:建图+最大流
解题报告:

  这道题的建图方式比较简单。第一问的话跑一下DP就可以了,nlogn的最长不下降子序列DP(n^2也可以),可以求出最大长度maxl。第二问考虑能从原序列中取出多少个长度为maxl的不下降序列。显然当我们选择了某个元素a[i]时,我们下一个可以选择的a[j],则a[j]是不小于当前元素且f[j]+1=f[i],我们考虑如何建图能达到题意要求。首先每个元素肯定只能用一次,并且只能按照上述顺序选取。

  建图:每个点i我们拆成两个点i.a和i.b。对于每个f[i]=maxl的i我们从S向i.a连一条容量为1的边,f[i]=1的i我们从i.b向T连一条容量为1的边。并且每个点内部i.a向i.b连一条容量为1的边。然后对于每个i,都要从i.b向所有满足f[j]+1=f[i]且a[j]>=a[i]的j中的j.a都连一条容量为1的边。这样建完图之后,我们跑一遍最大流即可求出最多能取出多少个长度为maxl的不下降子序列。

  考虑正确性。首先把每个点拆成两个,内部容量为1,则可以保证每个点最多被选一次。S向f[i]=maxl的点连边,可以保证我们取出的起点一定是有解而且正确的。f[i]=1同理。如此以来,我们就能得出第二问的答案。

  第三问的话实际上就是在第二问的基础上加以改进。因为第一个元素和最后一个元素可以取多个,那么我们只需要去掉对于这两个元素的个数约束就可以了。所以S向1、n的边,1、n向T的边,内部的边,全部把容量改为无穷大就可以了。其余的和第二问没有区别。

//It is made by jump~
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#ifdef WIN32
#define OT "%I64d"
#else
#define OT "%lld"
#endif
using namespace std;
typedef long long LL;
const int MAXN = 520;
const int MAXM = 500011;
const int inf = (1<<30);
int n,a[MAXN],f[MAXN];
int b[MAXN],maxl;
int S,T,ecnt,ans;
int first[MAXN*2],deep[MAXN*2];
queue<int>Q;
struct edge{
int to,next,f;
}e[MAXM];

inline int getint()
{
int w=0,q=0;
char c=getchar();
while((c<'0' || c>'9') && c!='-') c=getchar();
if (c=='-')  q=1, c=getchar();
while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
return q ? -w : w;
}

inline void link(int x,int y,int z){
e[++ecnt].next=first[x]; first[x]=ecnt; e[ecnt].to=y; e[ecnt].f=z;
e[++ecnt].next=first[y]; first[y]=ecnt; e[ecnt].to=x; e[ecnt].f=0;
}

inline int find(int x){
int l=1,r=maxl; int mid,ji=-1;
while(l<=r) {
mid=(l+r)/2;
if(b[mid]>=x) l=mid+1,ji=mid;
else r=mid-1;
}
if(ji==-1) return 0;
return ji;
}

inline void build(){//建图,S向f[i]=maxl的点连边,每个点拆成两个,i拆为i和i+n,f[i]=1的点向T
S=2*n+1; T=S+1;  ecnt=1;
for(int i=1;i<=n;i++) {
link(i,i+n,1);
if(f[i]==maxl) link(S,i,1);
else if(f[i]==1) link(i+n,T,1);
for(int j=i+1;j<=n;j++) {
if(a[j]>=a[i] && f[j]+1==f[i]) link(i+n,j,1);
}
}
}

inline bool BFS(){
while(!Q.empty()) Q.pop();
memset(deep,0,sizeof(deep));
Q.push(S); deep[S]=1;
while(!Q.empty()) {
int u=Q.front(); Q.pop();
for(int i=first[u];i;i=e[i].next) {
int v=e[i].to;
if(e[i].f && !deep[v]) deep[v]=deep[u]+1,Q.push(v);
}
}
if(!deep[T]) return false;
return true;
}

inline int maxflow(int x,int remain){
if(x==T || remain==0) return remain;
int flow=0,f;
for(int i=first[x];i;i=e[i].next) {
if(e[i].f && deep[e[i].to]==deep[x]+1) {
int v=e[i].to;
f=maxflow(v,min(remain,e[i].f));
if(f) {
flow+=f; e[i].f-=f; e[i^1].f+=f;
remain-=f;
if(remain==0) return flow;
}else deep[v]=-1;
}
}
return flow;
}

inline void build_new(){
memset(first,0,sizeof(first));
S=2*n+1; T=S+1;  ecnt=1;
for(int i=1;i<=n;i++) {
if(i==1 || i==n) {
link(i,i+n,inf);
if(f[i]==maxl) link(S,i,inf);
else if(f[i]==1) link(i+n,T,inf);
}
else{
link(i,i+n,1);
if(f[i]==maxl) link(S,i,1);
else if(f[i]==1) link(i+n,T,1);
}
for(int j=i+1;j<=n;j++) {
if(a[j]>=a[i] && f[j]+1==f[i]) link(i+n,j,1);
}
}
}

inline void work(){
n=getint(); for(int i=1;i<=n;i++) a[i]=getint();
for(int i=n;i>=1;i--) {
f[i]=find(a[i])+1;
if(b[f[i]]==0) b[f[i]]=a[i];
else b[f[i]]=max(a[i],b[f[i]]);
maxl=max(maxl,f[i]);
}
printf("%d\n",maxl);

build();
while(BFS()) ans+=maxflow(S,inf);
if(ans==0) ans=n;
printf("%d\n",ans);

ans=0;
build_new();
while(BFS()) ans+=maxflow(S,inf);
if(ans==0) ans=n;
printf("%d\n",ans);
}

int main()
{
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: