最长不下降子序列及输出序列

引入

回忆一下,以前学动态规划是的最长不下降子序列,我们是如何地推的呢???

First

2 n 2^n 2n暴力,判断每个是否选择输出最大值。

Second

记忆化搜索,时间复杂度待定。

Third

n 2 n^2 n2动态规划,代码如下。

#include<bits/stdc++.h>
using namespace std;
int a[1010],n,f[1010],ans;
int main()
{
    
    
	scanf("%d",&n);
	for (int i = 1;i <= n;++i)
		scanf("%d",a + i);
	for (int i = 1;i <= n;++i)
	{
    
    
		f[i] = 1;
		for(int j = 1;j < i;++j)
		{
    
    
			if (a[j] <= a[i] && f[i] < f[j] + 1)
				f[i] = f[j] + 1;
		}
		if(f[ans] < f[i])
			ans = i;
	}
	printf("%d\n",f[ans]);
	return 0;
}

可是,当 n n n很大时, n 2 n^2 n2也过不了,那这么办呢???

思路

利用序列的单调性

对于任意一个单调序列,如 12345 1 2 3 4 5 12345(是单增的),若这时向序列尾部增添一个数 x x x,若 x > 5 x>5 x>5,增添成功,反之则失败。
  由于普通代码是从头开始比较,而 x x x 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 的大小比较是没有用处的,这种操作只会造成时间的浪费,所以效率极低。
  优化方法就是新开一个数组 d,用它来记录每个序列的末尾元素,以求最长不下降为例, d k d_k dk 表示长度为k的不下降子序列的最小末尾元素。
  我们用 c n t cnt cnt表示当前最长序列长度,也就是当前 d d d中的最后那个位置。
  于是对于每个 a i a_i ai我们都找到一个刚刚比它大的 d i d_i di,可以证明这一定是最优解。
  输出最后 c n t cnt cnt

朴素时间

O ( N 2 ) O(N^2) O(N2)

二分优化:

当我们再找对于每个 a i a_i ai我们都找到一个刚刚比它大的 d i d_i di 时,我们可以利用单调性二分。
O ( N ∗ l o g ( n ) ) O(N*log(n)) O(Nlog(n))

代码:

    for(int i=1;i<=n;i++){
    
    
        int t=lower_bound(d,d+cnt+1,a[i]) - d;
        if(t>cnt) cnt=t;
        d[t]=a[i];
    }//最后输出cnt

输出序列:

    for(int i=1;i<=n;i++){
    
    
        int t=lower_bound(d,d+cnt+1,a[i]) - d;
        if(t>cnt) cnt=t;
        f[i]=t;//f数组代表第i个位结尾的最长不下降子序列长度
        d[t]=a[i];//当长度最大时记录d数组就是序列
    }

例题:

【NOIP2013模拟联考10】独立集(bubble)

猜你喜欢

转载自blog.csdn.net/zhy_Learn/article/details/110244722