LIS 最长不降子序列(二分篇)

LIS 最长不降子序列(二分篇)

就不说基本的N^2做法了
来说说二分做法,这个做法的复杂度(nlogn)
具体的看代码注释

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main(){
	int n,a[100005],f[100005],ans=0;//这里的a记录读入的数组,f这个数组来记录当前这个
	//最长上升子序列最后一个数 
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]; //常规读入没啥好说的
	}
	int l,r,mid;//这里用到二分的思想
	memset(f,-1,sizeof(f));//首先初始化数组为-1,为什么是-1呢?(往下看) 
	for(int i=1;i<=n;i++){
		if(f[ans]<a[i]){//我们这里对于每一个ai,都要找到一个序列,使这个ai大于等于
		//这个f[ans](这个子序列的末尾,就是这个序列中最大的一个) 
			f[ans+1]=a[i];//如果找到了,更新,这个子序列的长度++ 
			ans++;
			}
		else{//如果这个数不符合,那么你就要重新找,此时我们以ai为立足点,我们要找离
		//ai最近的子序列,为什么呢? 
		//这是因为我们运用了贪心的思想后能保证长度越大的
		//不上升序列结束点越小,试着用当前数去更新长度为
		//x的不上升序列的结束点(又是贪心的思想,只更新长度最长且结束点小于自己的
			l=0,r=ans;//二分查找 
			while(l<r){
				mid=(l+r)/2;
			if(f[mid]>=a[i]) r=mid;//如果这个满足,那么我们既然要找最右边的那个
			//,所以就把查找区间变成右半边 
			else{
				l=mid+1;//如果没找到,就变成左半边 
			}	
		}
		f[l]=a[i];
	} 
}
	cout<<ans<<endl;
	}

我们来优化一下,这里优化仅仅指代码量,这里介绍两个STL函数。
upper_bound  和 lower_bound
upper_bound(first,last,val)
这个函数,查找在first-last这个区间内,第一个大于val的值(以指针的形式返回)
lower_bound(first,last,val)
这个函数,查找在first-last这个区间内,第一个大于等于val的值(以指针的形式返回)
准确的说
lower_bound:返回指向范围 [first, last) 中首个不小于(即大于或等于) value 的元素的迭代器,或若找不到这种元素则返回 last 。
upper_bound:返回指向范围 [first, last) 中首个大于 value 的元素的迭代器,或若找不到这种元素则返回 last

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main(){
    int n,a[100005],f[100005],x=0,ans=0;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    memset(f,0x7f,sizeof(f));
    f[0]=-1;
    for(int i=0;i<n;i++){
        int v=lower_bound(f,f+ans+1,a[i])-f;
        ans=max(ans,v);
        f[v]=min(f[v],a[i]);//这个f[v],存长度为v的序列的最后一个数 
             

    }
    cout<<ans<<endl;
}

这个代码的逻辑和上一个代码的逻辑类似
对了!有个小tips,在使用upper_bound  和 lower_bound的时候
最后在括号外面一定要减一个f
因为是迭代器
还有就是,这个读入和循环的时候,应该是从0开始,这个指针是默认指队头0的

然后呢
给大家推荐一下练习题
[(https://www.luogu.org/problemnew/show/P1020)]

最后给大家安利一下大佬的博客
[https://me.csdn.net/Ted_Tong]
这个人今天也写了一篇关于LIS的博客,可以对比看看

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值