最长不下降子序列

最近想复习dp,于是先来水一篇博客。。。

如何来求一个一串长度为\(n\)的数列的最长不下降子序列呢?

\(dp[i]\)表示所有长度为\(i\)的不下降子序列的最小结尾数字。

也就是说\(dp[i]=\{min(j)|len[j]=i\}\)\(len[j]\)表示以\(j\)结尾的不下降子序列长度(实际代码并不要用上这个数组)。

然后就可以愉快的开始dp啦!

初始化\(dp[1]=a[1],ans=1\),从2开始dp

对于每一个\(a[i]\),若\(a[i]\ge dp[ans]\),那么它就可以更新当前答案,所以就令\(dp[++ans]=a[i]\)

若小于呢?则我们需要用上dp数组的单调性了。

我们先来看看dp数组的定义:\(dp[i]=\{min(j)|len[j]=i\}\)

再结合更新过程,我们显然可以得出\(dp[i]\ge dp[i-1]\)的性质

所以如果小于,我们就在\(dp[1\,\,to\,\,ans]\)中找到第一个大于\(a[i]\)的数,把它替换成\(a[i]\)

此时,因为它是第一个大于\(a[i]\)的,所以它之前的数肯定都小于\(a[i]\),所以\(a[i]\)完全可以代替它,且因为\(a[i]\)比它要小,所以带来的价值会更高(贪心)。

具体实现我们可以用STL中的upper_bound(毕竟学的都是C with STL 滑稽.jpg)。时间复杂度\(O(n\,\,log\,\,n)\)

Code:

#include<bits/stdc++.h>
using namespace std;
int n,ans;
int a[100001],dp[100001];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	dp[1]=a[1];ans=1;
	for(int i=2;i<=n;i++){
		if(a[i]>=dp[ans]) dp[++ans]=a[i];
		else {
			int x=upper_bound(dp+1,dp+ans+1,a[i])-dp;
			dp[x]=a[i];	
		}
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2019-01-30 19:52  DQY_dqy  阅读(292)  评论(0编辑  收藏  举报