您的位置:首页 > 其它

最长递增子序列问题

2017-10-30 20:26 183 查看

题目描述

«问题描述:

给定正整数序列x1,...,xn 。

(1)计算其最长递增子序列的长度s。

(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。

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

«编程任务:

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:
复制
4
3 6 2 5


输出样例#1: 复制
2
2
3


说明

n≤500n\le 500n≤500

摘自洛谷题解https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P2766

写的很清楚

[b]首先动态规划求出F[i],表示以第i位为开头的最长上升序列的长度,求出最长上升序列长度K。[/b]

[b]1、把序列每位i拆成两个点<i.a>和<i.b>,从<i.a>到<i.b>连接一条容量为1的有向边。[/b]

[b]2、建立附加源S和汇T,如果序列第i位有F[i]=K,从S到<i.a>连接一条容量为1的有向边。[/b]

[b]3、如果F[i]=1,从<i.b>到T连接一条容量为1的有向边。[/b]

[b]4、如果j>i且A[i] < A[j]且F[j]+1=F[i],从<i.b>到<j.a>连接一条容量为1的有向边。[/b]

[b]求网络最大流,就是第二问的结果。把边(<1.a>,<1.b>)(<N.a>,<N.b>) (S,<1.a>)(<N.b>,T)这四条边的容量修改为无穷大,再求一次网络最大流,就是第三问结果。[/b]

[b]【建模分析】[/b]

[b]上述建模方法是应用了一种分层图的思想,把图每个顶点i按照F[i]的不同分为了若干层,这样图中从S出发到T的任何一条路径都是一个满足条件的最长上升子序列。[/b]

[b]由于序列中每个点要不可重复地取出,需要把每个点拆分成两个点。单位网络的最大流就是增广路的条数,所以最大流量就是第二问结果。[/b]

[b]第三问特殊地要求x1和xn可以重复使用,只需取消这两个点相关边的流量限制,求网络最大流即可。[/b]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct Node
{
int next,to,c;
}edge[2000001],edge2[2000001];
int num=1,head[2001],cur[2001],n,m,dist[2001],ans,sum,maxflow,a[2001];
int f[2001],s,inf=2e9;
void add(int u,int v,int c)
{
num++;
edge[num].next=head[u];
head[u]=num;
edge[num].to=v;
edge[num].c=c;
}
bool bfs(int S,int T)
{int i;
memset(dist,-1,sizeof(dist));
queue<int>Q;
Q.push(S);
dist[S]=1;
while (!Q.empty())
{
int u=Q.front();
Q.pop();
for (i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if (edge[i].c>0&&dist[v]==-1)
{
dist[v]=dist[u]+1;
Q.push(v);
}
}
}
if (dist[T]==-1) return 0;
return 1;
}
int dfs(int x,int flow,int des)
{
int res=0;
if (x==des) return flow;
for (int &i=cur[x];i;i=edge[i].next)
{
int v=edge[i].to;
if (dist[v]==dist[x]+1&&edge[i].c)
{
int tmp=dfs(v,min(flow-res,edge[i].c),des);
if (tmp<0) continue;
edge[i].c-=tmp;
edge[i^1].c+=tmp;
res+=tmp;
if (res==flow) return res;
}
}
return res;
}
void Dinic(int S,int T)
{
maxflow=0;
while (bfs(S,T))
{
memcpy(cur,head,sizeof(cur));
int a=0;
while (a=dfs(S,2e9,T)) maxflow+=a;
}
return;
}
int main()
{int i,j;
cin>>n;
for (i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for (i=1;i<=n;i++)
{
for (j=0;j<=i-1;j++)
if (a[i]>=a[j])
f[i]=max(f[i],f[j]+1);
s=max(s,f[i]);
}
for (i=1;i<=n;i++)
if (f[i]==1) add(0,i,1),add(i,0,0);
for (i=1;i<=n;i++)
if (f[i]==s) add(n+i,2*n+1,1),add(2*n+1,n+i,0);
for (i=1;i<=n;i++)
add(i,n+i,1),add(n+i,i,0);
for (i=1;i<=n;i++)
for (j=1;j<=i-1;j++)
if (a[i]>=a[j]&&f[i]==f[j]+1)
add(n+j,i,1),add(i,n+j,0);
memcpy(edge2,edge,sizeof(edge));
Dinic(0,2*n+1);
cout<<s<<endl;
cout<<maxflow<<endl;
memcpy(edge,edge2,sizeof(edge));
for (i=head[1];i;i=edge[i].next)
{
int v=edge[i].to;
if (v==n+1)
{
edge[i].c=inf;
}
if (v==0)
{
edge[i^1].c=inf;
}
}
for (i=head[2*n];i;i=edge[i].next)
{
int v=edge[i].to;
if (v==n)
{
edge[i^1].c=inf;
}
if (v==2*n+1)
{
edge[i].c=inf;
}
}
Dinic(0,2*n+1);
cout<<maxflow;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: