最近想复习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:
#includeusing 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(a+1,a+ans+1,a[i])-a; dp[x]=a[i]; } } printf("%d\n",ans); return 0;}