java动态规划算法--最长公共子序列问题

基本概念:将一个问题,分解成多个阶段来解决,每一个阶段的决策都依赖于当前的状态,决策过后状态又发生了转移,这种多阶段来解决最优化问题的过程就是动态规划。

 

能用动态规划求解的问题一般要具有3个性质:

(1)、最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称为该问题的最优子结构,即满足最优化原理。

(2)、无后效性:即某阶段状态一旦确定,就不受这个状态的以后的决策影响,也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。

(3)、有重叠子问题:即子问题之间是不独立的,一个子问题在一阶段的决策中肯能多次被用到。(该性质并不是动态规划所必需的条件,但是如果没有该性质,动态规划算法较其他算法没有优势)。由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存到一个二维数组中。

求解步骤:

(1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。

    (2)确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。

    (3)确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。

(4)寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。整个求解过程就可以用一个最优决策表来描述,最优决策表是一个二维表,其中行表示决策的阶段,列表示问题状态,表格需要填写的数据一般对应此问题的在某个阶段某个状态下的最优值(如最短路径,最长公共子序列,最大价值等),填表的过程就是根据递推关系,从1行1列开始,以行或者列优先的顺序,依次填写表格,最后根据整个表格的数据通过简单的取舍或者运算求得问题的最优解。

f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}


以下是最长公共子序列问题解法,

引进一个二维数组c[][],用c[i][j]记录X[i]与Y[j] 的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向
我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。此时我们根据X[i] = Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。

问题的递归式写成:


recursive formula

回溯输出最长公共子序列过程:

flow

 

算法分析:
由于每次调用至少向上或向左(或向上向左同时)移动一步,故最多调用(m * n)次就会遇到i = 0或j = 0的情况,此时开始返回。返回时与递归调用时方向相反,步数相同,故算法时间复杂度为Θ(m * n)。


package ceshi;
public class LCS {
public static void main(String[] args) {
String[] x={"a","b","e","t","q"};
String[] y={"b","e","p","o","t"};
int[][] b = getLength(x,y);
Display(b,x,x.length-1,y.length-1);
}
public static int[][] getLength(String[] x,String[] y){
int[][] b = new int[x.length][y.length];
int[][] c = new int[x.length][y.length];

for(int i=1;i<x.length;i++){
c[i][0]=0;
}
for(int j=0;j<y.length;j++){
c[0][j]=0;
}
for(int i=1;i<x.length;i++){
for(int j=1;j<y.length;j++){
if(x[i]==y[j]){
c[i][j]=c[i-1][j-1]+1;
b[i][j]=1;
}else if(c[i-1][j]>=c[i][j-1]){
c[i][j]=c[i-1][j];
b[i][j]=0;
}else{
c[i][j]=c[i][j-1];
b[i][j]=-1;
}
}
}
return b;
}
public static void Display(int[][] b,String[] x,int i,int j){
if(i==0 || j==0){
return;
}
if(b[i][j]==1){
Display(b,x,i-1,j-1);
System.out.println(x[i]+"  ");
}else if(b[i][j]==0){
Display(b,x,i-1,j);
}else if(b[i][j]==-1){
Display(b,x,i,j-1);
}
}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值