二分求最长上升子序列(求长度)
之前在书上其实见到过,但是没想去看,结果…
回到正题,二分求最长上升子序列的思路是,在原动态规划写法上进行优化,优化了查找过程,使得查找过程变成了lon2n,然后还用到了贪心,怎么贪,就是保证原序列长度不变的情况下,让里面的元素尽可能小(当然满足递增),下面来验证为什么是这样是正确的
从大佬博客那里盗了一组数据
2 1 5 3 6 4 8 9 7 8 9 10
最长上升子序列
序列 长度
2 1
1 1
1 5 2
1 3 2
1 3 6 3
1 3 4 3
1 3 4 8 4
1 3 4 8 9 5
1 3 4 7 9 5
这上一变化要注意了,咋一看以为是错的,其实没错,把8替换成7,只是进行了替换,并不改变长度,但是如果使得数字尽可能小的话,长度就有可能变长,注意这里换的是序列中第一个大于等于(也就是尽可能靠左)该数字的数字,
1 3 4 7 8 5
1 3 4 7 8 9 6
1 3 4 7 8 9 10 7
实现的话倒是不难,一个数组存输入数组,一个数组模拟以上过程,再写个二分就完事了。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#define min(a,b) a<b?a:b
#define in(x) scanf("%d",&x)
#define out(x) printf("%d",x)
using namespace std;
const int maxn=1e6+5;
int a[maxn]={0},b[maxn]={0},len=1;//len用来记录
int bs(int l,int r,int num)
{
int mid,temp;
while(l<=r){
mid=(l+r)>>1;
if(b[mid]>num) r=mid-1;
else if(b[mid]<num){
l=mid+1;
temp=l;
}
else{
temp=mid;
break;
}
}
return temp;
}
int main()
{
int n;
in(n);
for(int i=1;i<=n;i++){
in(a[i]);
}
b[len]=a[1];
for(int i=2;i<=n;i++){
if(a[i]>b[len]){//如果比末位还大那就直接放入
b[++len]=a[i];
}
else if(a[i]<b[len]){//否则进行替换操作,这里有可能遇到相等的
int temp=bs(1,len,a[i]);//从左边起第一个大于等于该数字的数字
b[temp]=a[i];
}
}
out(len);
}
/*
7
2 5 3 4 1 7 6
*/