通过最长公共子序列和矩阵连乘来分析DP和备忘录
DP是什么
将一个问题拆成几个子问题,分别求解这些子问题,即可推断出大问题的解
定义:
动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。
动态规划算法的基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。
最长公共子序列(LCS)
题目
给出两个字符串,求出这样的一个最长的公共子序列的长度:子序列中的每个字符都能在两个原串中找到, 而且每个字符的先后顺序和原串中的先后顺序一致。
分析
包含重复子问题:
我们要求x1xi,y1yj的LCS,那么是不是要求x1xi-1,y1yi-1的LCS
我们要求x1xi-1,y1yi-1的LCS,那么是不是要求x1xi-2,y1yi-2的LCS
所以我们要求的x1xi,Y1Yj的LCS这个大问题中,包含了很多的重复子问题
核心
c[i][j]表示x1xi,y1yi的LCS序列长度
if(x[i]==y[j])
c[i][j]=c[i-1][j-1]+1
else
c[i][j]=Math.max(c[i-1][j],c[i][j-1])
if(i==0 || j==0)
c[i][j]=0
代码
package com.pub;
import java.util.ArrayList;
import java.util.List;
public class LCS {
public static List<String> getLCSstring(char[] str1, char[] str2) {
int i, j;
int len1, len2;
len1 = str1.length;
len2 = str2.length;
int maxLen = len1 > len2 ? len1 : len2;
int[] max = new int[maxLen];
int[] maxIndex = new int[maxLen];
int[] c = new int[maxLen];
List<String> list = new ArrayList<>();
//生成矩阵
for (i = 0; i < len2; i++) {
for (j = len1 - 1; j >= 0; j--) {
if (str2[i] == str1[j]) {
if ((i == 0) || (j == 0))
c[j] = 1;
else
c[j] = c[j - 1] + 1;
} else {
c[j] = 0;
}
if (c[j] > max[0]) { //如果是大于那暂时只有一个是最长的,而且要把后面的清0;
max[0] = c[j];
maxIndex[0] = j;
for (int k = 1; k < maxLen; k++) {
max[k] = 0;
maxIndex[k] = 0;
}
} else if (c[j] == max[0]) { //有多个是相同长度的子串
for (int k = 1; k < maxLen; k++) {
if (max[k] == 0) {
max[k] = c[j];
maxIndex[k] = j;
break; //在后面加一个就要退出循环了
}
}
}
}
}
for (j = 0; j < maxLen; j++) {
if (max[j] > 0) {
StringBuffer sb = new StringBuffer();
for (i = maxIndex[j] - max[j] + 1; i <= maxIndex[j]; i++) {
sb.append(str1[i]);
}
String lcs = sb.toString();
list.add(lcs);
}
}
return list;
}
public static void main(String[] args) {
String str1 = new String("adbba12345");
String str2 = new String("adbbf1234sa");
List<String> list = getLCSstring(str1.toCharArray(), str2.toCharArray());
for (int i = 0; i < list.size(); i++) {
System.out.println("第" + (i + 1) + "个公共子串:" + list.get(i));
}
str1 = new String("adbab1234");
str2 = new String("adbbf123s4a");
list = getLCSstring(str1.toCharArray(), str2.toCharArray());
for (int i = 0; i < list.size(); i++) {
System.out.println("第" + (i + 1) + "个公共子串:" + list.get(i));
}
}
}
矩阵连乘
题目
给定n个矩阵{A1,A2…An},其中Ai与Ai+1是可以相乘的,判断这n个矩阵通过加括号的方式相乘,使得相乘的次数最少!
例如:计算三个矩阵连乘{A1,A2,A3};维数分别为10100 , 1005 , 5*50
按此顺序计算需要的次数((A1A2)A3):10X100X5+10X5X50=7500次
按此顺序计算需要的次数(A1(A2A3)):10X5X50+10X100X50=75000次
所以要解决的问题是:如何确定矩阵连乘积A1A2,……An的计算次序,使得按此计算次序计算矩阵连乘积需要的数乘次数达到最小化。
分析
假设我们把这个问题看做一个多阶段决策问题,每次的决策就是选一个位置加一个括号。
假设我们已经得到最优的解了。那么这个最优解的最后一次乘法,一定可以看成是两个部分的乘积。而两个部分又可以看做是原问题的规模减小后的问题。
定义OPT(i,j)表示AiAi+1…Aj 所需的最少的乘法次数。原问题其实就是在求OPT(1,n)
而将原问题分解为两个子问题:OPT(1,k)和OPT(k,n),那么原问题的解可以通过O PT(1,n)=OPT(1,k)+OPT(k,n)+p0×pk×pn 得到。
伪代码
find_min_alpha(1,n)
for i=1 to n do
table[i,i]=0
end for
for j=2 to n do
for i=1 to n-j do
min=p[i-1]p[i]p[i+1]
for k=i to i+j do
tmp =table[i,k]+table[k+1,i+j] +p[i-1]p[k]p[i+j]
if tmp<min then
min=tmp
end if
end for
table[i,i+j]=min
end for
end for
return table[1,n]

浙公网安备 33010602011771号