【模板】O(nlongn)求LIS
合理运用单调性降低复杂度
平常用的都是O(n^2)的dp求LIS(最长不下降子序列)
这里介绍O(nlogn)的算法
分析
- 对于可能出现的
x<y<i
且A[y]<A[x]<A[i]
,则x相对于y更有潜力- 因为接下来可能出现
A[y]<A[z]<A[x]
而x<z<i
;
- 因为接下来可能出现
- 我们以
f[i]
表示前i
个数中的LIS最长长度;- 当出现
f[x]==f[y]
时,就应该选择x
而不会是y
- 当出现
- 我们可以得到以下思想
- 首先根据
f[]
值分类,记录满足f[t]=k
的最小的值a[t]
,记d[k]=min{a[t]},f[t]=k
. - 1.发现
d[k]
在计算过程中单调不上升 - 2.
d[1]<d[2]<...<d[k]
(反证) 1 2 3 8 4 7
- 首先根据
- 由此得到解法
- 设当前最长递增子序列为len,考虑元素a[i];
- 若
d[len]<a[i]
,则len++
,并将d[len]=a[i]
;否则,在d[0-len]
中二分查找,找到第一个比它小的元素d[k]
,并令d[k+1]=a[i]
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 41000;
int a[N]; //a[i] 原始数据
int d[N]; //d[i] 长度为i的递增子序列的最小值
int BinSearch(int key, int* d, int low, int high) {
while(low<=high) {
int mid = (low+high)>>1;
if(key>d[mid] && key<=d[mid+1])
return mid;
else if(key>d[mid]) low=mid+1;
else high=mid-1;
}
return 0;
}
int LIS(int* a, int n, int* d) {
int i,j;
d[1]=a[1];
int len=1; //递增子序列长度
for(i=2; i<=n; i++) {
if(d[len]<a[i]) j=++len;
else j=BinSearch(a[i],d,1,len)+1;
d[j]=a[i];
}
return len;
}
int main() {
int t;
int p;
scanf("%d",&t);
while(t--)
{
scanf("%d",&p);
for(int i = 1; i <= p; i++) scanf("%d",&a[i]);
printf("%d\n",LIS(a,p,d));
}
return 0;
}
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可.
发布者: bbqub
转载请注明出处: https://bbqub.cnblogs.com/