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

来源:互联网 发布:淘宝助理上架历史宝贝 编辑:程序博客网 时间:2024/05/20 12:21
问题:
求一个序列中的最长严格递增子序列。
解法:
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;elsehigh=mid-1;}/*low为<=a[i]中,最大值的下标,也就是可以为最长序列的长度*/maxa[low]=a[i];if(low>len)/*最长序列+1 的更新*/  len++;}






原创粉丝点击