问题描述:
给出一个数列,找出其中最长的单调递减(或递增)子序列。比如数列 4,2,6,3,1,5 最长递增子数列为2,3,5
相似的问题:1求最大和的子序列
2最大公共子串
3最大公共子序列
注意:串与序列的关系,,串是连续的,而序列则不可以。。。
解决办法
:在网上得到两种解法
解法一:
首先将这个序列进行排序,然后再求出已排序的和未排序的这两个数列最长公共子序列
解法二:就是用动态规划法来解决问题:
我们假设原数列定义为数组a[],然后定义一个数组b[i],表示以a[i]结尾的最长公共子序列的长度,只要求出所有的b[i]数组也就可以确定最长的公共子序列,
那么我们根据已经求出的b[0]--b[i-1],来求b[i]呢,,也就如何求出以a[i]结尾的最大公共子序列的长度呢,
此时在a[0]-a[i-1]范围内,已经求出多个长度的公共子序列,且最大长度的一个序列的长度为s,这个序列的最后一个元素为t,那么
1.如果a[i]<t ,那么当前最大子序列的长度为s+1,而且这个序列的最后一个元素就是a[i],即b[i]=s+1
2.如果a[i]=t,那么这个子序列的长度不变,仍为s,b[i]=s;
3.如果a[i]>t,这时候就会比较的麻烦了,因为a[i]不可能成为长度为s的最长公共子序列的最后一项了,然后与一个长度为s-1的公共子序列的最后一项进行比较。。直至小于某一个长度为s1的子序列的最后一个元素t1,
如果a[i]仍然大于t1,那么就继续向长度更小的子序列的最后一个元素进行比较,知道找到一个合适的子序列长度s2,那么此时b[i]=s2+1;如果a[i]比所有的以求子序列的最后一个元素都大话,那么最后就赋值b[i]=1;即以该元素为结尾的子序列长度为1.
那么如何保存这些已经查找出来的子序列的最后一一个元素呢 ,我们定义一个数组c[],即c[i]就是代表长度为i的当前最长子序列的最后一个元素,我们最终的元素超找比较就是在c[]数组中进行的,b[i]最后可以用于最终的最长子序列的输出。
按照上述的条件得到的c[i]肯定是单调递增或者递减的,如果采用二分查找的话那么时间复杂度为O(n*lgn)..
动态的转移公式为:
假设f(i)代表以a[i]结尾的最长单调自增子序列的长度:
/ f(i-1)+1 , a[i]>a[i-1]
f(i)= --- f(i-1) ,a[i]==a[i-1]
\ max(max({f(j)|a[i]<a[j],i>j}+1),1) ,a[i]<a[i-1]
比如查找 4,2,6,3,1,5 的最长递减子数列过程如下:
最终的数组b[]={1,2,1,2,3,1}
数组c[]={-1,6,5,1,-1,-1,}
具体的算法如下:
void getArray(int *a,int length)
{
int *b=new int[length]; //b[i]表示以a[i]结尾的最长递减子序列的长度
int *c=new int[length];//数组c[i]表示长度为i的递减子序列的末尾值的最大值
int s=1;//s表示目前为止最长单调序列的长度
int k=1;
for(int i=0;i<length;i++)//初始化b
{
b[i]=-1;
}
c[1]=a[0];
for(int j=0;j<length;j++)
{
k=BinFind(c,s,a[j]);
if(k>s)
{
s++;
}
c[k]=a[j];
b[j]=k;
}
print(a,b,s,length);//根据数组b[]来打印最长子序列
}
void print(int *a,int *c,int s,int length)//从后向前查找,并打印出来
{
int i=length;
while(s>0)
{
while(c[i]!=s&&s>0)
{
if(i<=0)
{
break;
}
i--;
}
printf("%d ",a[i]);
s=s-1;
i--;
}
}
下面在来说下几个相关的问题:
1.最大子序列问题
就是求出连续的子序列的和最大,数组中有正数也有负数。。。。这个问题在之前已经描述过了。。
就是遍历数组,保证序列的前面部分的和为大于零
最后比较每个序列的和的大小并选择之
2.最长公共子串(LCS)
这个问题主要是找出两个序列之中相同的最长的连续子串比如:abcad abaad 最长公共子串包括ad与ab
如何解决这个问题呢:
在网上见到一个比较新颖的解决方案:用矩阵的形式,如下:
以一个二维数组来表示对应位置相同的位1,不同的为零
b a b
c 0 0 0
a 0 1
0
b 1
0 1
a 0 1
0
可以看见相同的子序列在一条斜线上都为1.所以最长公共子序列为ab,ba
在这种二位数组中寻找这种相同的元素也是比较困难的,,所以就修改到如下方式:
b a b
c 0 0 0
a 0 1
0
b 1
0 2
a 0 2
0
把斜线上的1加到加到最后一个元素位置上,但是考虑到算法的效率,这样会浪费很多无谓的空间,所以我们使用一维数组来描述二维数组:
定义两个一位数组,我们按行进行初始化,其中一个数组保存上一行的数据,另外一个数组保存当前行的数据,当前行的数据就是通过上一行来进行计算,最后找出最大的值
2.最长公共子串(LCS)
最常用的就是动态规划法:
/ 0 if i<0 or j<0
c[i,j]=--- c[i-1,j-1]+1 if i,j>=0 and xi=xj
\ max(c[i,j-1],c[i-1,j] if i,j>=0 and xi≠xj
使用s[i][j]数列来保存c[i][j]使用上述哪种方式得到的,,,最后找出s[i][j]中所有位1为i,j(即是由i-1,j-1得到的c[i][j])
分享到:
相关推荐
这篇实验报告主要探讨了在数据与算法领域中如何找到数列的最长递增子序列,这是一个常见的算法问题,尤其在计算机科学的算法设计与分析中占有重要地位。实验的目的是通过设计、实现和分析算法来锻炼解决问题的能力。...
1. **最长递增子序列**:寻找数列中最长的一段,使得这段序列中的元素都是递增的。这是动态规划的经典问题,可以使用“保持最优点”或“保持边界”等策略来解决。 2. **分组求和**:要求将数列分成若干组,使得每组...
栈可以用来实现后进先出,帮助追踪递增或递减子序列;队列则适用于处理先进先出的情况,如广度优先搜索。 在实际编程实现时,良好的编程习惯和代码可读性至关重要。清晰的变量命名、合理的函数划分以及适当的注释能...
例如,可能需要找出数列中最长的连续子序列,使得子序列的所有元素之和不超过一个给定的阈值,或者找到满足特定条件的最大子序列数量。 解这类问题时,我们需要理解并熟练运用以下知识点: 1. **数组和链表**:...
3. 1157_DP.cpp的源码可能涉及到的问题可能是“背包问题”的变种,比如“完全背包”或“多重背包”问题,或者可能是“最长递减子序列”等。在解决这类问题时,DP通常会结合贪心策略,通过选择具有最大效益的元素来...
- 逆向思考:对于一些问题,逆向考虑可能更简单,例如最长递减子序列问题。 6. **代码实现** 在实际编程中,理解问题的关键在于定义好状态和状态转移方程。例如,对于斐波那契数列,可以定义dp[i]为第i个斐波那契...
这是最长递增子序列(Longest Increasing Subsequence)的一个变种,不仅考虑数值上的递增,还引入了两个属性(x, y),要求在x递增的同时y递减,或x和y同时递增但不在同一集合中。这个问题通过使用映射(map)来存储特定...
1.5.8. 给出一个数列,找出其中最长的单调递减(或递增)子序列..............121 1.5.9. 四对括号可以有多少种匹配排列方式.................................................124 1.5.10. 输入一个正数 n,输出...
2. **线性 DP**:处理一维数组的问题,如斐波那契数列、最长递增子序列等。 3. **区间 DP**:处理二维或多维区间问题,例如区间最大子段和。 4. **计数类 DP**:计算满足特定条件的组合或排列的数量。 5. **数位统计...
674.最长连续递增序列(2020/7/3) 697.数组的度(2020/7/4) 717.1比特与2比特字符(2020/7/4) 724.寻找数组的中心索引(2020/9/27) 746.使用最小花费爬楼梯(2020/10/9) 747.至少是其他数字两倍的最大数(2020/10/9) 766....