动态规划并不是一种算法,而是一种解决问题的思路。典型的动态规划问题,如最长公共子序列(LCS),最长单调子序列(LIS)等。
动态规划分为四个步骤:
1.判断问题是否具有最优子结构
这里以LCS为例,X={x1,x2,...,xi};Y={y1,y2,...,yj}。最长公共子序列Z={z1,z2,...,zk};
①如果xi=yj,那么zk=xi=yj,且Zk-1是序列Xi-1和Yj-1的LCS;
②如果xi≠yj,那么zk≠xi;且Zk是序列Xi-1和Yj的LCS;
③如果xi≠yj,那么zk≠yj;且Zk是序列Xi和Yj-1的LCS;
可以用反证法证明上述子结构的成立。
2.一个递归解
设c[i,j]是Xi和Yj的LCS的长度,i=0或j=0时,c[i,j]=0;
c[i,j]=0 (i=0或j=0)
c[i,j]=c[i-1,j-1] (i,j>0;xi=yj)
c[i,j]=max{c[i-1,j],c[i,j-1]} (i,j>0;xi≠yj)
3.计算LCS的长度
c[i,j]为递归解,那么有多少个不同的递归解呢?O(m*n)。即要有重叠子问题,而不是每次都要解决新的问题。至于重叠子问题,需从底向上求。每次只需索引之前较小规模的子问题即可。
从底向上,求解c[i,j]。还需维护b[i][j]以简化最优解的结构。
例如,设所给的两个序列为X=<A,B,C,B,D,A,B>和Y=<B,D,C,A,B,A>。由算法LCS_LENGTH和LCS计算出的结果如图2所示。
package hello; /* * 本代码通过动态规划法获取2个序列之间的最长公共子序列,并输出序列的长度。 * 通过动态规划法解决的问题往往都具有最优子结构性质和重叠子问题性质。 * 本题的最优子结构性质证明如下: * 对于2个序列的最长公共子序列。如果x[m]=y[n],则最长公共子序列就是x[m-1]和y[n-1]的最长公共子序列+1。 * 如果x[m]和y[n]不相等,那么最长公共子序列就是x[m]和y[n-1]或x[m-1]和y[n]之间最长公共子序列的最大值。 * 如果子序列的公共子序列不能达到最大,则该公共子序列一定不是最大的,与假设矛盾。 * * 递归方程为:c[i][j]=0 当 i=0,或j=0 * c[i][j]=c[i-1][j-1]+1 当i,j>0;x[i]=y[j]; * max{c[i][j-1],c[i-1][j]} i,j>0;x[i]不等于y[j] * * * */ public class getLongestString { public static void getLongest(char[] x,char[] y,int[][] longest,int[][] ch){ //将最长序列的边界值进行初始化 for(int i=0;i<x.length;i++) longest[i][0] = 0; for(int i=0;i<y.length;i++) longest[0][i] = 0; //由底向上根据刚刚的递推公式一层一层求解longest[i][j],通过ch[i][j]来记录字母 for(int i=1;i<x.length;i++) for(int j=1;j<y.length;j++){ if(x[i] == y[j]) {longest[i][j] = longest[i-1][j-1]+1;ch[i][j]=1;} else if(longest[i-1][j]>longest[i][j-1]){ longest[i][j] = longest[i-1][j]; ch[i][j]=2; } else { longest[i][j] = longest[i][j-1]; ch[i][j] = 3; } } System.out.println("最长公共子序列长度为:"+longest[x.length-1][y.length-1]); System.out.print("该序列是:"); getString(x.length-1,y.length-1,x,y,ch); } //获取最长的公共子序列,当出现第一种情况时,就可以提取出一个字母 public static void getString(int i,int j,char[] x,char[] y,int[][] ch){ if(i==0 || j==0) return; else if(ch[i][j]==1) { getString(i-1,j-1,x,y,ch); System.out.print(x[i]); } else if(ch[i][j]==2) getString(i-1,j,x,y,ch); else getString(i,j-1,x,y,ch); } public static void main(String[] args){ String str1 = " skowcgk"; String str2 = " dsofdwvk"; char[] ch1 = str1.toCharArray(); char[] ch2 = str2.toCharArray(); int[][] longest = new int[ch1.length][ch2.length]; int[][] ch = new int[ch1.length][ch2.length]; getLongest(ch1,ch2,longest,ch); } }
时间: 2024-10-27 13:51:13