求一个序列中的最长严格递增子序列。

本文探讨了求解最长严格递增子序列问题的多种算法思路,包括基础的动态规划方法及其优化版本,最终给出一种时间复杂度为O(nlogn)的高效算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:
求一个序列中的最长严格递增子序列。
解法:
1、最为朴素的我们可以想到,可以枚举每一中子序列,贪心地往后面找序列。回溯,再找。


2、我们思考对n个序列,如果前面n-1个序列都覆盖了,那么多添加第n个序列有多少种可能性情况?
可能性一:第n个太小了,小到比前n-1个都小,没有哪个序列会以它为后继。
可能性二:第n个太大了,大到比前n-1个都大,每一序列都贪婪地想以它为后继,这时选择最长的序列。
可能性三:第n个不大不小,跟前n-1个没有很严格的大小关系。依旧选择最长的序列,但此时选的这个序列的最大值要比它小,它才能名正言顺地当后继。

综合得到的状态方程:dp[i] = max(1,dp[j]+1) and a[j] < [i] and j = 1,2,3..i-1,可见:max后面()中代表的是一个集合。

/*复杂度为O(n^2)*/
		int dp[100];
		memset(dp,0,sizeof(dp));
		dp[1] = 1;
		for(int i = 2;i <= n;i++)
		{
			int tmp = 0;
			for(int j = 1;j < i;j++)
			{
				if(a[i] > a[j] && tmp < dp[j])
					tmp = dp[j];
			}
			dp[i] = tmp+1;
		}

3、继续思考,由于“max后面()中代表的是一个集合”,我们需要对这个集合进行一次遍历,其实也就是上面代码中的内层循环。
有没有办法让这个集合有点规律?让我们查找时间不再是O(n)的呢?
方法是有的,就是新开辟一个数组b。
首先想一个问题:比如序列4 2 5 3,可见3 和 5 这两个数都是序列长度为2的子序列的末尾,那么任意加一个数放在后面,是该说它为3的后继好?还是说为5的后继好?
显然是3,因为新加的是6的话,作为3、5的后继,长度一样,序列最后以为也是一样;但要是新加了一个4,则它只能为3的后继。
那么我们新开的数组b[i],就可以表示为序列长为i的“序列的集合中”,末尾最小的。
即:b[i] = min( a[end] ) and dp[end] = i
观察数组b:b[1]长为1的子序列中最后一位的最小值。b[2]长为2。。。
显然数组b必须是单调递增的。
那么在一个单调递增的数列中找一个数tmp,tmp为比新加的a[n]小,但tmp又是数列中(满足小于等于条件)为最大的。显然二分可以查找到tmp,查找复杂度降为O(logn)。总算法复杂度O(nlogn)

	/*复杂度O(nlogn)*/
	maxa[0]=INT_MIN;/*子序列中的最大值,即最后一位,为前文的数组b*/
	maxa[1]=a[1];
	len=1;
	for(int i=2;i<=n;++i)
	{
		int low=0,high=len;
		while(low<=high)
		{
			int mid=(low+high)/2;
			if(maxa[mid]<a[i])	low=mid+1;
			else				high=mid-1;
		}/*low为<=a[i]中,最大值的下标,也就是可以为最长序列的长度*/
		maxa[low]=a[i];
		if(low>len)/*最长序列+1 的更新*/
		  len++;
	}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值