简述
动态规划(dynamic programming)与分治方法相似,都是通过组合子问题的解来求解原问题。动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。在这种情况下,分治算法会做许多不必要的工作,它会反复地求解那些公共子子问题。而动态规划算法对每个子子问题只求解一次,将其解保存在一个表格中,从而无需每次求解一个子子问题时都重新计算,避免了这种不必要的计算工作。动态规划方法通常用来求解最优化问题(optimization problem)。这类问题可以有很多可行解,每个解都有一个值,我们希望寻找具有最优值最小值或最大值)的解。我们称这样的解为问题的一个最优解(an optimal solution),而不是最优解(the optimal solution),因为可能有多个解都达到最优值。
通常按如下4个步骤来设计一个动态规划算法:
1.刻画一个最优解的结构特征。
2.递归地定义最优解的值。
3.计算最优解的值,通常采用自底向上的方法
4.利用计算出的信息构造一个最优解。
钢条切割案例
package s21;
import java.util.Arrays;
/*钢条切割,动态规划
长度为n的钢条怎样切割使售卖价格最大
*/
public class DynamicProgramming {
//带备忘的自顶向下方法
public static int memoizedCutRod(int[] p , int n){
int[] r = new int[n+1]; //记录最优解
Arrays.fill(r,Integer.MIN_VALUE);
return memoizedCutRodAux(p,n,r);
}
public static int memoizedCutRodAux(int[] p , int n , int[] r){
int q;
if(r[n]>0) //当有记录时直接返回
return r[n];
if(n==0) //长度0,价值0
q = 0;
else{
q = Integer.MIN_VALUE;
for(int i = 1; i <= n; i++) //每次循环,切割第一段长度遍历
//递归取剩余长度的最优解
q = Math.max(q,p[i]+memoizedCutRodAux(p,n-i,r));
}
r[n] = q;
return q;
}
//自低向上法
public static int cutRod(int[] p, int n){
if(n==0)
return 0;
int q = Integer.MIN_VALUE;
for(int i = 1; i <= n; i++) //每次循环,切割第一段长度遍历
//递归取剩余长度的最优解
q = Math.max(q,p[i]+cutRod(p,n-i));
return q;
}
//自低向上法,记录最优解方案
public static void extendedCutRod(int[] p, int n){
int[] r = new int[n+1];
int[] s = new int[n+1];
int q;
r[0] = 0;
for(int j = 1; j <= n; j++){ //循环取每种长度的最大价值
q = Integer.MIN_VALUE;
for(int i = 1; i <= j; i++){ //遍历第一段方案
if(q<p[i]+r[j-i]){ //r[j-i]必处理过
q = p[i]+r[j-i]; //找到更优方案,更新价值
s[j] = i; //更新第一段长度
}
}
r[j] = q;
}
System.out.println(Arrays.toString(r));//[0, 1, 5, 8, 10]
System.out.println(Arrays.toString(s));//[0, 1, 2, 3, 2]
//切割方案
while(n>0){ //2 2
System.out.print(s[n] + " ");
n = n - s[n];
}
}
public static void main(String[] args) {
//索引长度对应价值
int[] p = {0,1,5,8,9,10,17,17,20,24,30};
System.out.println(memoizedCutRod(p,4)); //10
System.out.println(cutRod(p,4)); //10
extendedCutRod(p,4);
}
}
矩阵相乘最优解
import java.util.Arrays;
public class MatrixMultipleAnalysis {
public static void main(String[] args) {
/*矩阵相乘A1*A2*...AN,加()进行最优组合,
A(N-1) * AN 需要乘法次数m[n-1][n] = p[n-2] * p[n-1] * p[n]
迭代公式,i-j范围从k处拆解 m[i][j] = m[i][k] + m[k+1][j] + p[i-1] * p[k] * p[j]
*/
int[] p = {10,100,5,50};
matrixChainOrder(p);
}
public static void matrixChainOrder(int[] p){
int n = p.length - 1;
int[][] m = new int[n+1][n+1];
int[][] s = new int[n][n+1];
for(int i = 1; i <= n; i++)
m[i][i] = 0;
for(int l = 1; l <= n-1; l++) //括号内矩阵长度,从2个矩阵相乘扩展到n个
for(int i = 1; i < n-l+1 ; i++){ //括号内第一个矩阵位置
int j = i + l; //括号内最后一个矩阵位置
m[i][j] = Integer.MAX_VALUE;
for (int k = i; k <= j - 1; k++) { //循环拆分位置,取最优解
int q = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
if (q < m[i][j]) {
m[i][j] = q;
s[i][j] = k;
}
}
}
for(int i = 0; i <= n; i++)
System.out.println(Arrays.toString(m[i]));
System.out.println("------------------------------");
for(int i = 0; i < n; i++)
System.out.println(Arrays.toString(s[i]));
}
}
共同最长子集
public class CommonSubset {
public static int maxI = 0;
public static int maxJ = 0;
public static int max = 0;
public static void main(String[] args) {
String s1 = "ACCGGTCGAGTGCGCGGAAGCCGGCCGAA";
String s2 = "GTCGTTCGGAATGCCGTTGCTCTGTAAA";
char[] x = s1.toCharArray();
char[] y = s2.toCharArray();
longest(x,y);
}
public static void longest(char[] x, char[] y){
boolean[][] arr = new boolean[x.length][y.length];
int[][] count = new int[x.length][y.length];
for(int i=0;i<x.length;i++)
for(int j=0;j<y.length;j++){
if(x[i] == y[j]){
arr[i][j] = true;
if(i==0 || j==0)
count[i][j] = 1;
else {
//比较水平、垂直、对角+1位置的最长子集,取最大值
count[i][j] = count[i - 1][j - 1] + 1;
//记录最长子集最后一个元素位置
if (max < count[i][j]) {
max = count[i][j];
maxI = i;
maxJ = j;
}
if (count[i - 1][j] > count[i][j]) {
count[i][j] = count[i - 1][j];
arr[i][j] = false;
}
if (count[i][j - 1] > count[i][j]) {
count[i][j] = count[i][j - 1];
arr[i][j] = false;
}
}
}
else{
arr[i][j] = false;
if(i==0 || j==0)
count[i][j] = 0;
else {
count[i][j] = count[i - 1][j - 1];
count[i][j] = Math.max(count[i][j], count[i - 1][j]);
count[i][j] = Math.max(count[i][j], count[i][j - 1]);
}
}
}
//迭代进行字符串拼接GTCGTCGGAAGCCGGCCGAA
System.out.println(get(arr,count,maxI,maxJ,max,x));
}
public static String get(boolean[][] a,int[][] b, int maxI, int maxJ, int max,char[] x){
for(int i = maxI;i>=0;i--)
for(int j = maxJ;j>=0;j--) {
if (a[i][j] && b[i][j] == max) {
String s = get(a, b, i - 1, j - 1, max - 1, x);
if (s == null)
return Character.toString(x[i]);
else
return String.join("",s, Character.toString(x[i]));
}
}
return null;
}
}