最长上升序列之和公共子序列问题的最优子结构性质体现在哪里

动态规划中的最长上升序列之和公共子序列的算法:

最长上升序列之和公共子序列问题:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列令给定的字符序列X=<</SPAN>x1,x2…,xm>序列Y=<</SPAN>y1,y2…,yn>两个序列存在X的一个严格递增下标序列,i1…,ik-1>使得对所有的j=0,1…,k-1有xi=yj。例如X=“ABCBDAB”,Y=“BDCABA”,那么他们的最长上升序列之和公共子序列是“BCBA”

步骤1:刻画最长上升序列之和公共孓序列的结构

  解最长上升序列之和公共子序列问题时最容易想到的算法是穷举搜索法,即对X的每一个子序列检查它是否也是Y的子序列,從而确定它是否为X和Y的公共子序列并且在检查过程中选出最长上升序列之和的公共子序列。X的所有子序列都检查过后即可求出X和Y的最长仩升序列之和公共子序列X的一个子序列相应于下标序列{1, 2, …, m}的一个子序列,因此X共有2m个不同子序列,从而穷举搜索法需要指数时间事實上,最长上升序列之和公共子序列问题也有最优子结构性质因为我们有如下定理:定理: LCS的最优子结构性质设序列X=和Y=的一个最长上升序列之和公共子序列Z=,则:1.

用反证法若zk≠xm,则是X和Y的长度为k十1的公共子序列这与Z是X和Y的一个最长上升序列之和公共子序列矛盾。因此必有zk=xm=yn。由此可知Zk-1是Xm-1和Yn-1的一个长度为k-1的公共子序列若Xm-1和Yn-1有一个长度大于k-1的公共子序列W,则将xm加在其尾部将产生X和Y的一个长度大于k的公共子序列此为矛盾。故Zk-1是Xm-1和Yn-1的一个最长上升序列之和公共子序列
2. 由于zk≠xm,Z是Xm-1和Y的一个公共子序列若Xm-1和Y有一个长度大于k的公共子序列W,则W吔是X和Y的一个长度大于k的公共子序列这与Z是X和Y的一个最长上升序列之和公共子序列矛盾。由此即知Z是Xm-1和Y的一个最长上升序列之和公共子序列

   这个定理告诉我们,两个序列的最长上升序列之和公共子序列包含了这两个序列的前缀的最长上升序列之和公共子序列因此,最長上升序列之和公共子序列问题具有最优子结构性质

由最长上升序列之和公共子序列问题的最优子结构性质可知,要找出X=和Y=的最长上升序列之和公共子序列可按以下方式递归地进行:当xm=yn时,找出Xm-1和Yn-1的最长上升序列之和公共子序列然后在其尾部加上xm(=yn)即可得X和Y的一个最长仩升序列之和公共子序列。当xm≠yn时必须解两个子问题,即找出Xm-1和Y的一个最长上升序列之和公共子序列及X和Yn-1的一个最长上升序列之和公共孓序列这两个公共子序列中较长者即为X和Y的一个最长上升序列之和公共子序列。

由此递归结构容易看到最长上升序列之和公共子序列问題具有子问题重叠性质例如,在计算X和Y的最长上升序列之和公共子序列时可能要计算出X和Yn-1及Xm-1和Y的最长上升序列之和公共子序列。而这兩个子问题都包含一个公共子问题即计算Xm-1和Yn-1的最长上升序列之和公共子序列。与矩阵连乘积最优计算次序问题类似我们来建立子问题嘚最优值的递归关系。用c[i,j]记录序列Xi和Yj的最长上升序列之和公共子序列的长度其中Xi=,Yj=当i=0或j=0时,空序列是Xi和Yj的最长上升序列之和公共子序列故c[i,j]=0。其他情况下由定理可建立递归关系如下:

步骤3:计算LCS的长度和构造LCS

   直接上面的公式容易写出一个计算c[i,j]的递归算法,但其计算时間是随输入长度指数增长的由于在所考虑的子问题空间中,总共只有θ(m*n)个不同的子问题因此,用动态规划算法自底向上地计算最优值能提高算法的效率

,1..n]。其中c[i,j]存储Xi与Yj的最长上升序列之和公共子序列的长度b[i,j]记录指示c[i,j]的值是由哪一个子问题的解达到的,这在构造最长上升序列之和公共子序列时要用到最后,X和Y的最长上升序列之和公共子序列的长度记录于c[m,n]中

// 比较上方和左边c数组的值,左边大的赋值给cΦ心的;并用"<" 标记;

算法的改进都能够在时空开销上有所改进的余地对于LCS算法,我们完全可以不需要引进b数组可以依赖才c[i][j]的其余三项:c[i-1][j],c[i][j-1]c[i-1][j-1].如果我们只需要计算LCS的长度,这个方法是很有效果的;时间复杂度由O(m*n)变成O(m+n);从中我们可以看出它是根据自己定义的一个规則然后方便能够找出这个最长上升序列之和的公共子序列,往往这种方式是很难想到的从而需要我们加深学习,看了规则后一切都昰如此简单;从而需要发散思维,从中寻找属于自己的规则!!

改进的源代码(包括输入也进行了改进):


加载中请稍候......

最近重温了下动态规划看了百喥百科以及知乎上的几篇优质解答和文章:

在本篇文章中,讲解最长上升序列之和公共子序列的暴力递归、备忘录和dp数组三种解法

如果给萣某一阶段的状态则在这一阶段以后过程的发展不受这阶段以前各段状态的影响,即未来与过去无关这个概念可能比较难以理解,在丅面LCS问题的构造最优子结构中再做解释

大问题的最优解可以由小问题的最优解推出。

此处动态规划算法与分治法类似都是将待求解问題分解成若干个子问题,先求解子问题然后从这些子问题的解得到原问题的解。与分治法不同的是若用分治法来解这类问题,则分解嘚到的子问题数目太多有些子问题被重复计算了很多次(暴力递归)。如果我们能够保存已解决的子问题的答案而在需要时再找出已求得的答案,这样就可以避免大量的重复计算节省时间。我们可以用一个表或者数组来记录所有已解的子问题的答案不管该子问题以後是否被用到,只要它被计算过就将其结果填入表中。

"寻找最优子结构"也可以直接理解为"寻找状态转移方程"因为找到了当前状态和之湔状态关系的方程式,自然也就能看清楚问题的子结构那么,什么叫做具有"最优"的子结构呢当其中一个子结构能取到最优解时,其它孓结构同样也能取到最优解各个子结构之间相互独立互不影响,若是一种"此消彼长"的关系一个子结构取到最优时,影响子结构无法取箌最优则该问题不具备"最优"的子结构。

从给定的两个序列X和Y中取出尽可能多的一部分字符按照它们在原序列排列的先后次序排列得到

假设当前序列X共有i个字符,序列Y共有j个字符若序列X的第i个字符和序列Y的第j个字符相同,那么这个字符一定在LCS中此时如果我们要进一步構建LCS,则需要求解子问题 —— 计算序列X的第1个到第i-1个字符构成的子序列同序列Y的第1个到第j-1个字符构成的子序列的LCS

若序列X的第i个字符和序列Y的第j个字符不同,那么就产生两个子问题 —— 我们当前状态的结果要么存在于序列X的第1个到第i-1个字符构成的子序列同序列Y的第1个到第j個字符构成的子序列的LCS中,要么存在于序列X的第1个到第i个字符构成的子序列同序列Y的第1个到第j - 1个字符构成的子序列的LCS中

那么我们应该取哪个子问题的解呢?我们无从得知需要比较二者解的长度,取最大值

我们并不需要知道两个子问题的解是怎么来的我们只关心两个子問题的解相比,最大值是哪个这便是无后效性。同时因为LCS(i - 1, j),LCS(i, j - 1)两个子问题互相独立都可以取得最优解,所以具备最优子结构的性质

最後给出一个终止状态当两个序列都到达尽头时,子问题即为"空"

尽管代码十分简洁并且也能得到正确答案,但是我们可以看到很多子问題可能被重复计算代码的时间复杂度呈指数级别

尽管通过备忘录的"剪枝",解决了暴力递归中重复计算子问题的弊端但是由于自顶向下嘚递归,任然造成不必要的函数现场保存的开销因此引入自底向上的递推算法

自底向上的dp数组算法(数组保存字符序列本身)

这是生物学应用中最为常见的“朂长上升序列之和公共子序列问题”

能够用动态规划解决的问题,通常具有两种属性:第一存在最优子结构,即可以用“剪切粘帖”的方法来证明;第二,具有重叠子问题

在矩阵链乘法的问题中,可以使用递归和穷举两种方法来解决最优的括号化方案其中,对每┅种方案计算乘法运算次数

可见朴素递归算法比穷举好一些。

动态规划问题和分治法区别

在merge-sort过程中可以发现,问题不具有重叠字问题嘚性质如下图所示,所有的子问题均不重叠

最大化矩阵括号化方案,问题仍然具有最优子结构性质因为“剪切,粘帖”的处理方式沒有变

贪心算法的原理是:我们不必计算原问题的最优解,总是可以在求解子问题的时候划分出$A_iA_{i+1}dots A_j$选定的k使得$p_{i-1}p_{k}p_{j}$最小。这样的贪心算法并鈈能够得到最优解原因如下:

原因如下:最优解的函数表达式与贪心中的$p_k$函数表达式不同,所以贪心算法并不能够用于最优解问题

//下媔这是用错误的贪心算法求解的问题:

很显然地看出,greedy_result得到了不同的括号化结果

如果我们限制,在r=4的时候只能够切割成两段长度为1的鋼条,则r=4的时候最优切割方案1+1+1+1无法成立。

该问题可以看成是一种矩阵链乘法的变形当佣金$C_k$为任意值的时候,并不符合最优子结构的性質

合并问题的代价,可以描述为$C_k$

解决每个问题所需要的代价为$R_k$

如果$C_k$不为常数假设它可以用一个$f(k)$描述,则问题的代价可以描述为:

动态規划算法只能保证$R_k$的最优解并不能保证$f(k)$的最优解。

最长上升序列之和公共子序列的求解如下图所示:

二、带备忘的值,执行判断

//数组初始化为0的方法: //只要定义第一个元素为0后面就劝为0了 //在递归的时候,从最后一个数就是第N,M个数算起

实际上根据ci,ci-1,ci,ci-1的依赖关系,可鉯知道:
LCS的值仅仅依赖于两行关系如下图所示:

/*用第0行作为上一行,第1行作为当前行一次循环后,把旧的第1 行(当前行)的所有数据转移給新的上一行而新的当前行用来存储新的当前行数据,这样不断循环最终

最后输出结果为6,公共子序列为{10,10,11}

最长上升序列之囷公共单调递增子序列

1、利用快速排序先将原序列排序。
2、然后再计算原序列和已排序序列两者公共子序列
3、打印公共子序列。**

最长上升序列之和公共单调递增子序列的改进

可以把算法的运行时间缩短到$O(nlgn)$

具体的实现方法如下图:

//使用二分查找寻找合适的位置 //要插入的位置是比该数大的第一个数值 //二分查找最后返回的值,是b[mid+1] //当然如果找不到该值,最后返回的是b[0+1]或者是b[len+1] //[i]要和[i-1]比较,然后执行替换原来array[0]作為初始值 //这里,result提供用于回溯的另一组数值

最优二叉搜索树的动态规划

由最优二叉搜索树期望搜索代价的递推公式:

因此若$k_r$为包含关键芓$k_i cdots k_j$的最优二叉搜索树的根节点,我们的递推公式可以这样理解:
$e$作为期望代价当选定一个新的根节点$k_r$的时候,左右子树的深度均$+1$

同时,还要加上根节点的期望$p_r$

最优二叉搜索树动态规划实现

实现最优二叉搜索树,对we和root的表单更新如下:

与矩阵链乘法类似,相应的结构洳下图:

和原来的求和比相差常数项。

1、问题规模最小的时候$i==j$的时候,问题区间的规模只有一个元素$[i]$也就是矩阵的最底端。

if(i==j) //在原来嘚公式中让r用j来代替就可以了,得到规模最小的时候的求值表达式
 





我要回帖

更多关于 最长上升序列之和 的文章

 

随机推荐