最长递增子序列
来源:互联网 发布:手机财经软件 编辑:程序博客网 时间:2024/06/02 01:41
文字部分转载:
一 问题描述
设序列L = <a1, a2, a3, ..., an>是长度为n的序列,L的一个递增序列描述为:<ai1, ai2,..., aik>, 其中下标序列 <i1, i2, ..., ik>是
递增的, 子序列<ai1, ai2, ...., aik> 也是递增的。此递增序列的长度为 k
二 解法1, 转化为LCS问题
先把序列 L 按照从小到大的顺序排列, 得到另一个序列S,再求L和S的最长公共子序列
三 解法2,动态规划
另 len[i] 表示以第 i 个元素结尾的最长递增子序列的长度,最后求出 max { len[i] } 即可
实例 <1, 3, 4, 2, 7>
len[1] = 1, 以1结尾的最长递增子序列是<1>, 长度为1
len[2] = 2, 以3结尾的最长递增子序列是<1,3>, 长度为2
len[3] = 3, 以4结尾的最长递增子序列是<1, 3, 4>, 长度为3
len[4] = 2, 以2结尾的最长递增子序列是<1, 2>,长度为2
那么,如何求len[5]呢?转化为下面的子问题:
第5个元素等于7,找到一个序号在a[5]前面且小于a[5]的元素a[i], 以a[i]结尾的最长递增子序列加上a[5], 组成一个新的最长递增子序列,其
长度比原来的多1。比a[5]小的元素有多个(最多4个),那么得到的递增子序列也有多个,其中最长的那个就等于以a[5]结尾的最长递增子序列。
例中,a[3 ]比a[5]小,而且以a[3]结尾的最长递增子序列是最长的, 故以a[5]结尾的递增子序列长度在其基础上加1,等于4
len[5] = 4, 以5结尾的最长递增子序列是<1,3,4,5>,长度为4
四 解法3,对解法2的改进
解法2中,求len[i]的时候, 要从a[1], ... a[i-1]中找出所有比 a[i] 小的元素,而a[1], ... a[i-1]是无序的。查找速度比较慢。
引出数组 f[k] 表示长度等于k的递增子序列中最末尾的元素, 长度越长的序列,其末尾元素也越大,所以f[k]是递增的。
实例 <1, 3, 4, 2, 7>
len[1] = 1, f[1] = a[1] = 1
len[2] = 2, f[2] = a[2] = 3
len[3] = 3, f[3] = a[3] = 4
len[4] = 2, f[2] = a[4] = 2 ( 更新了f[2] )
那么,如何求len[5] 呢?
f[1] = 1, f[2] = 2, f[3] = 4, a[5] = 7,
找出末尾元素比a[5]小,而且长度最长的递增子序列,此例中,
f[3] = 4 表示长度等于3的子序列,其末尾元素为4,这个子序列的长度最长。
a[5]加上此序列形成的新序列长度为4, 然后更新f[4] = a[5] = 7,表示长度
等于4的子序列,其末尾元素等于7
在上面的步骤中,找出末尾元素比a[i]小,而且长度最长的子序列,其实就是
对于f[1], ...f[k], 从后往前找,第一个比a[i]小的元素就是长度最长的子序列。查找的时候
可以用二分查找方法。故比解法1更快。
自己不好的代码:
#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>using namespace std;int a[100],n;void Input(){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}}int MAX(int x,int y){if(x<y) return y;else return x;}void trans_lcs(int *b)//O(n*n+n*logn){int bb[100];int dp[100][100];memset(dp,0,sizeof(dp));for(int i=1;i<=n;i++)bb[i]=b[i];sort(bb+1,bb+n+1);dp[0][0]=0;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(b[i]==bb[j]) dp[i][j]=dp[i-1][j-1]+1;else dp[i][j]=MAX(dp[i][j-1],dp[i-1][j]);} cout<<dp[n][n]<<endl;}void dynamic(int *b)//O(n*n){int len[100];int MAX;memset(len,0,sizeof(len));len[1]=1;for(int i=2;i<=n;i++){MAX=-1;for(int j=1;j<i;j++){if(MAX<len[j]&&b[i]>b[j])//如果是非递减的当找到b[i]=b[j]是len[i]=len[mark] {MAX=len[j];}}len[i]=MAX+1;}MAX=-1;for(int i=1;i<=n;i++){if(MAX<len[i]){MAX=len[i];}}cout<<MAX<<endl;}void binary_dynamic(int *b)//O(n*logn){int f[100];//f[k]长度为k的递增子序列的最后一个元素值。int len[100];//len[k]以第k个元素结尾的递增子序列的长度。int length=1;//当前最长子序列的长度。 len[1]=1;f[1]=b[1];for(int i=2;i<=n;i++){int now;int lft=1,rht=length;//找到在f中第一个比a[i]小的,更新他后面的一个 while(lft<=rht){int mid=(lft+rht)>>1;if(b[i]>=f[mid])//严格递增就把"="去掉{now=mid;lft=mid+1;}else {rht=mid-1; now=mid; }}f[lft]=b[i];if(lft>length)//都比它小,最长子序列长度增加 { length++;len[i]=length; }else len[i]=len[lft];} for(int i=1;i<=n;i++){cout<<len[i]<<" ";}cout<<endl;cout<<length<<endl;}int main(){int t;scanf("%d",&t);while(t--){Input();cout<<"转化为LCS问题:"; trans_lcs(a);cout<<"动态规划:";dynamic(a); cout<<"动态规划二分优化:";binary_dynamic(a); }return 0;}
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 最长递增子序列
- 优秀Android开源项目大全
- C++ 字符集转换留着用
- 11235 - Frequent values (RMQ)
- Scala集合一些操作
- 恢复默认设置对语音控制选项无效
- 最长递增子序列
- Enumeration遍历
- Spark 1.1.0 Basic Statistics(下)
- java学习笔记之容器的同步与只读控制
- Highcharts数据列(Series)
- linux shell 自定义函数(定义、返回值、变量作用域)介绍
- 洛谷1290 欧几里德的游戏
- 不重叠模式串个数 KMP hdu 2087 剪花布条
- Unity3D:spine使用