最长上升子序列 LIS
Description
给出一个 1 ∼ n (n ≤ 10^5) 的排列 P
求其最长上升子序列长度
Input
第一行一个正整数n,表示序列中整数个数;
第二行是空格隔开的n个整数组成的序列。
Output
最长上升子序列的长度
Sample Input
7
1 7 3 5 9 4 8
Sample Output
4
解析
这题(O)((n^2))很容易就能想到,
然而,(1e5)却会炸掉....
所以,考虑二分.
我们维护一个类似于栈的数组(q)(其实是序列但为了方便懒得打字后面就称作栈吧)
令(q[i])表示长度为(i)的序列的最后一个元素,
那么,从(1)~(n)枚举,每次在(q)中寻找第一个大于等于(a[i])(即权值)的元素,
再用(a[i])去更新它,并且它的下标就是以(a[i])结尾的最长上升子序列.
而原因也很简单,对于(i)后面的元素(k)以及(i)更新掉的元素(j),
首先,根据算法,我们知道(a[i])<=(a[j]),且以(a[i])结尾的上升子序列长度等于以(a[j])结尾的上升子序列长度.
那么,对于(k),它接到(i)后面和接到(j)后面的效果(即长度)是一样的,
但是,如果(a[i])<(a[k])<(a[j]),那么(k)能接到(i)后面,却不能接到(j)后面,
所以,用(i)更新掉(j)一定是更优的,
并且,(j)的长度也是对于(i)来说最优的,
因为后面的接不上了.
于是最后,再从(1)~(n)扫一遍,取最大值就行了.
口胡证明自己理解下哈
上代码吧:
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return f*sum;
}
int n,a[100001],ans=0;
int f[100001],c[100001];
int main(){
n=read();
memset(c,0x3f,sizeof(c));
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++){
int k=lower_bound(c+1,c+n+1,a[i])-c;
f[i]=k;c[k]=a[i];
}
for(int i=1;i<=n;i++) ans=max(ans,f[i]);
printf("%d
",ans);
return 0;
}