最长上升子序列

最长上升子序列(Longest increasing subsequence)

问题描述
        对于一串数A={a1a2a3an} 它的子序列为S= {s1s2s3 sn} ,满足{s1<s2<s3<<sm} 。求A的最长子序列的长度。
这是一个很经典的dp模型了,一般来说,我们很容易想到O(n^2)的算法,也很通俗易懂;
dp[i]表示到i为止说能得到的最长上升子序列,这样我们每次计算dp[i]都能通过与前面的A[j](0<=j<i)比较来进行更新;
则可以的到dp方程:dp[i]=max(dp[j]+1)((a[j]<a[i])&&(0<=j<i));
但这不是今天的重点,因为我们还有更加优秀的算法;
ps:如果你已经理解的了这个算法抑或你对证明并没有什么兴趣,可以跳过这一段,直接看最后的模板;
我们可以通过维护一个数组d[i],来记录长度为i的子序列,末尾最小的一项
如:d[2]=5:表示长度为2的子序列,它末尾最小是5;
我们就可以得到这个d数组的两个特征了:
1.它是单调的,d[1]<d[2]<d[3]<……<d[n];
2.它在更新的过程中也是单调的,每次更新值一定比原来的值小;

特征一:很好理解,我们假设他不单调,即存在d[i]>d[j]&&i<j;
则可以推出矛盾,因为i<j,长度为j的子序列就会包含长度为i的子序列,又因为这个子序列随着长度的增加,数值也在增加,那么更长的j末尾的值就一定大于长度为i的值;
特征二:我们为什么要选择最小的来当作末尾的值呢?这里有一个小小的贪心,如果我选了最小的,那么后面我又得到了一个数x,x可以来被加在序列后面的条件是x>我已经的到的序列末尾的值;而d数组恰好是记的末尾那一个位置的,当末尾这一项尽量小的时候,我后面就可以金科能多的更新最多的值,使其构成最长的上升子序列;
每次寻找一个i最小的d[i]>x,然后用x来更新d[i];寻找我们可以用二分来优化;每次在更新的过程中我们可以通过记录我们二分的上限来表示我们的最长上升子序列可以达到的范围;
(是不是很机智,通过这种方法,这种方法不仅是时间上的优化,还能空间上的优化。)
下面贴一个代码模板:
<span style="white-space:pre">	</span>for(i=1;i<=n;i++){
		scanf("%d",&x);
		d[i]=(1<<30);
		l=1;r=k;
		while(l<=r){
			int mid=(l+r)>>1;
			if(d[mid]>x)r=mid-1;
			else l=mid+1;
		}
		if(d[l]>x){
			if(d[l]==(1<<30))k++;
			d[l]=x;
		}
	}
	printf("%d\n",k);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值