code vs [网络流24题] 最长递增子序列问题

1906 最长递增子序列问题

 时间限制: 2 s
 空间限制: 256000 KB
 题目等级 : 大师 Master
题目描述 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

注意:虽然题目是最长递增子序列,但是code vs上的测试数据是按最长不下降子序列来的

题解:

1. dp求最长不下降子序列,不用多说

2.建图(看程序,比较好理解),跑最大流

3.与2相似,因为1,n可以多次使用,所以只需加大源点到1的容量和n到汇点的容量即可

另外此题还是需要拆点,拆成的两个点之间连一条容量为1的边,因为只有这样才能控制每个点之用一次

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int point[2100],next[2000003],v[2000003],remain[2000003];
int deep[2100],cur[2100],num[2100],n,m,tot,ans,laste[2100];
int a[2100],f[2100];
const int inf=1e9;
void add(int x,int y,int z)
{
  tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z;
  tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
}
int adde(int s,int t)
{
	int now=t,minn=inf;
	while (now!=s)
	 {
	 	minn=min(minn,remain[laste[now]]);
	 	now=v[laste[now]^1];
	 }
	now=t;
	while (now!=s)
	 {
	 	remain[laste[now]]-=minn;
	 	remain[laste[now]^1]+=minn;
	 	now=v[laste[now]^1];
	 }
	return minn;
}
void bfs(int s,int t)
{
  for (int i=s;i<=t;i++)
   deep[i]=t+2;
  deep[t]=0;
  queue<int> p;
  p.push(t);
  while (!p.empty())
   {
   	int now=p.front(); p.pop();
   	for (int i=point[now];i!=-1;i=next[i])
   	 if (deep[v[i]]==t+2&&remain[i^1])
   	  {
   	  	deep[v[i]]=deep[now]+1;
   	  	p.push(v[i]);
   	  }
   }
}
int isap(int s,int t)
{
  int flow=0,now=s;
  bfs(s,t);
  memset(num,0,sizeof(num));
  for (int i=s;i<=t;i++)  num[deep[i]]++;
  for (int i=s;i<=t;i++)  cur[i]=point[i];
  while (deep[s]<=t)
   {
   	 if (now==t)
   	  {
   	  	flow+=adde(s,t);
   	  	now=s;
   	  }
   	 bool hasfind=false;
   	 for (int i=cur[now];i!=-1;i=next[i])
   	  {
   	  	if (deep[v[i]]+1==deep[now]&&remain[i])
   	  	 {
   	  	 	hasfind=true;
   	  	 	cur[now]=i;
   	  	 	laste[v[i]]=i;
   	  	 	now=v[i];
   	  	 	break;
   	  	 }
   	  }
   	 if (!hasfind)
   	  {
   	  	int minn=t;
   	  	for (int i=point[now];i!=-1;i=next[i])
   	  	 if (remain[i])
   	  	  minn=min(minn,deep[v[i]]+1);
   	  	if (!--num[deep[now]]) break;
   	  	deep[now]=minn;
   	  	num[minn]++; cur[now]=point[now];
   	  	if (now!=s)
   	  	 now=v[laste[now]^1];
   	  }
   }
  return flow;
}
void solve1()
{
  for (int i=1;i<=n;i++)
   {
   	if (f[i]==1)  add(0,i,1);
   	if (f[i]==ans) add(i+n,2*n+1,1);
   	add(i,i+n,1);
   }
  for (int i=2;i<=n;i++)
   {
   	 for (int j=1;j<i;j++)
   	  if (f[i]==f[j]+1&&a[i]>=a[j])
   	   add(j+n,i,1);
   }
  if (ans==1) printf("%d\n",n);
  else
  printf("%d\n",isap(0,2*n+1));
}
void solve2()
{
	tot=-1; memset(next,-1,sizeof(next)); memset(point,-1,sizeof(point));
	for (int i=1;i<=n;i++)
	 {
	 	int v=1;
	 	if (i==1||i==n) v=inf;
	 	if (f[i]==1) add(0,i,v);
	 	if (f[i]==ans) add(i+n,2*n+1,v);
	 	add(i,i+n,v);
	 }
	for (int i=2;i<=n;i++)
     {
   	 for (int j=1;j<i;j++)
   	  if (f[i]==f[j]+1&&a[i]>=a[j])
   	   add(j+n,i,1);
     }
    if (ans==1) printf("%d\n",n);
    else
    printf("%d\n",isap(0,2*n+1));
}
int main()
{
  tot=-1; memset(next,-1,sizeof(next)); memset(point,-1,sizeof(point));
  scanf("%d",&n);
  for (int i=1;i<=n;i++) scanf("%d",&a[i]);
  f[1]=1;
  for (int i=2;i<=n;i++)
  {
   for (int j=1;j<i;j++)
    if (f[i]<f[j]&&a[j]<=a[i])
     f[i]=f[j];
   f[i]++;
  }
  ans=0;
  for (int i=1;i<=n;i++)
   ans=max(ans,f[i]);
  printf("%d\n",ans);
  solve1();
  solve2();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值