LIS问题——n*logn解法

简述:
LIS问题,即最长上升子序列问题,经典的解法有序列DP,通过这个算法,可以获得最长上升子序列的各种详细信息。但是,我们有时候只需要求最长上升子序列的长度,但是o(n^2)的时间复杂度太慢了,我们希望有一种算法,可以更快一点。

算法过程
既然动态规划太慢了,那么自然就想到了贪心。下述算法,就是运用了二分+贪心
首先考虑一个序列a:1、3、2、7、5、6、4 (一共7个数),另外一个辅助数组d,d[i]记录长度为i的上升子序列第i位的最小元素(因为数组后续元素所在的上升子序列的第i个位置的使用元素必定是越小越好),最终序列长度为ans
第1个数:d[1] = a[1] = 1 , ans = 1 ;
第2个数:a[2] > a[1] , 所以d[2] = a[2] , ans = 2 ;
第3个数:d[1] < a[3] < d[2] , 此时ans不改变,但是根据贪心法则,d[2] = a[3],这是因为后续元素如果想组成长度为3的上升子序列,使用a[3]肯定优于d[2];
第4个数:a[4] > d[2], d[3] = a[4] = 7 , ans = 3 ;
第5个数:a[5] < d[3] , ans不变,根据贪心法则,d[3] = a[5] ;
第6个数:a[6] > d[4] , d[4] = a[6] , ans =4 ;
第7个数:a[7] < d[4] , 为保证算法完整性,d[3] = a[7] (这里替代的数是第一个大于key的数)。
此时ans=4,d数组为:1、2、4、6 。
虽然我们算出了答案,但是我们并不能得到最终的最长子序列,这就是这个算法的不足。

代码:
用poj 2533来测代码,题目链接:poj 2533

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int n,ans,a[1005],d[1005];

int upper(int key)			//二分找到第一个比key大的数
{
	int low=1,high=ans,mid;
	if(d[ans]<key)
		return ans+1;
	while(low<high)
	{
		mid=(low+high)/2;
		if(d[mid]<key)
			low=mid+1;
		else
			high=mid;
	}
	return high;
}

int main()
{
	while(~scanf("%d",&n))
	{
		memset(d,0,sizeof(d));
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		
		d[1]=a[1],ans=1;
		for(int i=2;i<=n;i++)
		{
			int pos=upper(a[i]);
			d[pos]=a[i];
			ans=max(ans,pos);
		}
		printf("%d\n",ans);
	}
	
	return 0;
}

总结:
运用二分的思想,虽然达不到找到最长子序列的效果,但是找到答案绰绰有余,所以这个算法还是有它的价值的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值