博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

题解 最长上升子序列 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;
}

posted @ 2019-03-27 10:14  Hastin  阅读(158)  评论(0编辑  收藏  举报