题目:
给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。
数据范围:
N<100010
-10^9<=序列中的数<=10^9
分析
q[i]表示长度是i的上升子序列最后一个数的最小值,q[]的长度就是最大可能的上升子序列长度
q[r + 1] = a[i]:这里并没有选择最小的而直接等于a[i]是因为可以保证“相等长度的上升序列,后来得到的序列的结尾数值一定小于或等于前面得到的”,假设先前得到的序列xxxa,后来得到的序列xxxb,a和b满足b在a的后面且b>a,那么显然xxxb一定不是以b结尾的最长上升子序列,正确序列中一定包含a,所以假设不成立,命题得证。
数据示例(来源于acwing)
输入:
7
3 1 2 1 8 5 6
执行过程:
- i=0, l=0, r=len=0 不满足二分条件 ——> len=r+1=1,q[1]=a[0]=3
- i=1, l=0, r=len=1 进行二分 ——> r=0, len=1,q[1]=a[1]=1
- i=2, l=0, r=len=2 进行二分 ——> r=1, len=2,q[2]=a[2]=2
- i=3, l=0, r=len=2 进行二分 ——> r=0, len=2,q[1]=a[3]=1
- i=4, l=0, r=len=2 进行二分 ——> r=2, len=3,q[3]=a[4]=8
- i=5, l=0, r=len=3 进行二分 ——> r=2, len=3,q[3]=a[5]=5
- i=6, l=0, r=len=3 进行二分 ——> r=3, len=4,q[4]=a[6]=6
结果为 4
代码
#include<iostream>
#include<iostream>
using namespace std;
const int N=100010;
int n;
int a[N]; //存储每个数
int q[N]; //存储不同长度下所有上升子序列结尾的最小值
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
int len=0; //q里面的元素个数
for(int i=0;i<n;i++) //枚举一下每个数
{
//需要二分出来小于某个数的最大的数 -- 如何二分?
//可以二分的原因,不同长度的最后一个数是严格单调递增的
int l=0,r=len;
while(l<r)
{
//这个二分是找右端点
int mid = l+r+1>>1;
if(q[mid]<a[i]) l=mid;
else r=mid-1;
}
len = max(len,r+1);
q[r+1] = a[i];
}
cout<<len;
return 0;
}

771

被折叠的 条评论
为什么被折叠?



