2018.7.13 Chengdu
学了一天的 DP D P 问题,啥都没听懂,但是勉强听懂了最长不下降子序列的求法(我太弱啦),特发于此。
二分查找的妙(误)用·最长不下降子序列
最长不下降子序列作为DP的简单线性问题之一,其最常见的解法是 O(n2) O ( n 2 ) 的暴力枚举。用数组 a(i) a ( i ) 表示该序列中的第 i i 项,用数组 f(i) f ( i ) 表示序列的前 i i 项的最长不下降子序列长度,并初始化 f(1)=1 f ( 1 ) = 1 。假设当前已经求出 [1,i) [ 1 , i ) 范围内的 f f 数组的值,接下来求 f(i) f ( i ) ,只需要扫描 [1,i) [ 1 , i ) 中每一个元素 j j ,如果有 a(j)≤a(i) a ( j ) ≤ a ( i ) ,那么 f(i)=max{ f(i),f(j+1)} f ( i ) = m a x { f ( i ) , f ( j + 1 ) } 。
所以可以很容易得到其 O(n2) O ( n 2 ) 代码:
#include<bits/stdc++.h>
using namespace std;
int n,x,a[1100000],f[1100000],ans;
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
f[i]=1;
for(int j=0;j<n;j++)
if(a[j]<=a[i]) f[i]=max(f[i],f[j]+1);
ans=max(ans,f[i]);
}
printf("%d\n",ans);
return 0;
}
那么我们如何使用二分查找来优化算法达到 O(nlogn) O ( n log n ) 呢?
先定义一个数组 f(i) f ( i ) ,初始化全为 +∞ + ∞ 。
这样的话怎么去更新数组呢?我们来拿一个序列做例子。这个序列的长度为 9 9 ,序列表示为: 。
i i | 2 2 | 4 4 | 6 6 | 8 8 | |||||
---|---|---|---|---|---|---|---|---|---|
f(i) f ( i ) | +∞ + ∞ | +∞ + ∞ | +∞ + ∞ | +∞ + ∞ | +∞ + ∞ | +∞ + ∞ | +∞ + ∞ | +∞ + ∞ | … … |
首先输入 3 3 ,按照升序插入数组 中。此时的 f f 中的值均为 ,故 3 3 最小,插入到 。
i i | 2 2 | 4 4 | 6 6 | 8 8 | |||||
---|---|---|---|---|---|---|---|---|---|
f(i) f ( i ) | 3 3 | +∞ + ∞ | +∞ + ∞ | +∞ + ∞ | +∞ + ∞ | +∞ + ∞ | +∞ + ∞ | … … |
接下来插入 5 5 , 比 3 3 要大,但显然比 要小,所以把它插入到 f(2) f ( 2 ) 。
i i | 2 2 | 4 4 | 6< |
---|