题解 最长上升子序列 LIS
最长上升子序列 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\n",ans);
return 0;
}