二分求最长上升子序列

二分求最长上升子序列(求长度)

之前在书上其实见到过,但是没想去看,结果…

回到正题,二分求最长上升子序列的思路是,在原动态规划写法上进行优化,优化了查找过程,使得查找过程变成了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
*/

 

猜你喜欢

转载自blog.csdn.net/DaDaguai001/article/details/87260830