您的位置:首页 > 理论基础 > 计算机网络

最长递增子序列问题

2017-03-16 20:46 417 查看

问题描述

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


编程任务

设计有效算法完成(1)(2)(3)提出的计算任务。


数据输入

由文件input.txt提供输入数据。文件第1行有1个正整数n,表示给定序列的长度。接下来的1行有n个正整数x1,···,xn。


结果输出

程序运行结束时,将任务(1)(2)(3)的解答输出到文件 output.txt中。第1 行是最长递增子序列的长度s。第2行是可取出的长度为s的递增
子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s的递增子序列个数。


输入文件示例

input.txt
4
3 6 2 5


输出文件示例

output.txt
2
2
3


题解

问题(1)可以用dp进行求解。

对于问题(2)和(3),我们可以用网络流解决。对于每一个数字,我们都看作节点并进行拆点,以达到限流的目的。对于每一个dp值为1,也就是以这个数为结尾的最长上升子序列长度为1,的节点,我们可以从源点引一条边连向这个点。同理,对于每一个dp值最大的节点,我们从它引一条边连向汇点。这种建图方式也蕴含了分层图的思想。(即从源点开始,bfs到的点分别是dp为1、2、3的点)

因为我们要构建分层图,所以对于每一个f[i]==f[j]+1,s[i]>=s[j] (j < i)的情况,我们从j拆点后的点引一条边连向i点(本人一开始连边的时候是从i的拆点指向j,但是去掉f[i]==f[j]+1的条件可以得到82分,我能说是数据太水了么)

CODE:

#include<cstdio>
#include<cstring>
const int INF=1e9;
struct queue
{
int h,t;
int a[10001];
inline void clear(){h=1,t=0;}
inline void push(int n){a[++t]=n;}
inline int front(){return a[h];}
inline void pop(){h++;}
inline bool empty(){return h>t;}
}q;
struct edge
{
int next,to,remain;
}a[200001];
int head[1001];
int deep[1001];
int s[1001];
int f[1001];
int n,num=1,ans,S,T;
inline int max(int a,int b){return a>b?a:b;}
inline int min(int a,int b){return a<b?a:b;}
inline void add(int x,int y,int cap)
{
a[++num].next=head[x],a[num].to=y,a[num].remain=cap,head[x]=num;
a[++num].next=head[y],a[num].to=x,head[y]=num;
}
inline bool bfs()
{
memset(deep,0x3f,sizeof(deep));
deep[S]=0;
q.clear();q.push(S);
while(!q.empty())
{
int tmp=q.front();q.pop();
for(int i=head[tmp];i;i=a[i].next)
if(deep[a[i].to]>INF&&a[i].remain)
q.push(a[i].to),deep[a[i].to]=deep[tmp]+1;
}
return deep[T]<INF;
}
int dfs(int now,int limit)
{
if(now==T||!limit) return limit;
int flow=0,f;
for(int i=head[now];i;i=a[i].next)
if(deep[a[i].to]==deep[now]+1&&(f=dfs(a[i].to,min(limit,a[i].remain))))
{
limit-=f,flow+=f,a[i].remain-=f,a[i^1].remain+=f;
if(!limit) return flow;
}
deep[now]=-1;
return flow;
}
inline int dinic()
{
int ans=0;
while(bfs()) ans+=dfs(S,INF);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&s[i]);
f[1]=1;
for(int i=2;i<=n;i++)
{
f[i]=1;
for(int j=1;j<i;j++)
if(s[j]<=s[i]) f[i]=max(f[i],f[j]+1);
ans=max(ans,f[i]);
}
printf("%d\n",ans);
//1~n:原点 n+1~2*n:拆点 2*n+1:源点 2*n+2:汇点
S=n<<1|1;T=S+1;
for(int i=1;i<=n;i++)
add(i,n+i,1);
for(int i=1;i<=n;i++)
{
if(f[i]==1) add(S,i,1);
if(f[i]==ans) add(n+i,T,1);
}
for(int i=2;i<=n;i++)
for(int j=1;j<i;j++)
if(s[j]<=s[i]&&f[j]==f[i]-1) add(n+j,i,1);
ans=dinic();printf("%d\n",ans);
for(int i=head[S];i;i=a[i].next)
if(a[i].to==1) {a[i].remain=INF;break;}
for(int i=head[1];i;i=a[i].next)
if(a[i].to==n+1) {a[i].remain=INF;break;}
for(int i=head[n<<1];i;i=a[i].next)
if(a[i].to==T) {a[i].remain=INF;break;}
for(int i=head
;i;i=a[i].next)
if(a[i].to==n<<1) {a[i].remain=INF;break;}
ans+=dinic();printf("%d",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络流24题