暴力递归改动态规划
例:给你一个二维数组,二维数组中的每个数都是正数,要求从左上角走到右下角,每一步只能向右或者向下。沿途经过的数字要累加起来。返回最小的路径和。
测试数组:
int[][] m = { { 3, 1, 0, 2 }, { 4, 3, 2, 1 }, { 5, 2, 1, 0 } };
1.写出尝试(递归)版本
/**
* 暴力递归---重复计算
* 从(i,j)出发,到达最右下角位置,最短路径和是多少(返回)
* @param matrix
* @param i: 二维数组行
* @param j : 二维数组列
*/
public static int walk(int[][] matrix, int i, int j) {
// 已经到达最右下角
if (matrix.length - 1 == i && j == matrix[0].length - 1) {
return matrix[i][j];
}// 到达最后一行,只能向右走
// 即该值下一个位置到达最右下角最短路径和 加上该值
if (matrix.length - 1 == i) {
return matrix[i][j] + walk(matrix, i, j + 1);
}// 到达最后一列,只能向下走
if (matrix[0].length - 1 == j) {
return matrix[i][j] + walk(matrix, i + 1, j);
}
// 可以向右走:当前值+右边位置到达最右下角最短路径和
int right = walk(matrix, i, j + 1);
// 可以向下走:当前值+下边位置到达最右下角最短路径和
int down = walk(matrix, i + 1, j);
return matrix[i][j] + Math.min(right, down);
}
2.分析可变参数,哪几个可变参数的值能代表返回状态,几个可变参数,就构造几维表。
在本题中,数组是不可变的,最小路径和只与i,j位置有关,i,j确定了,最小路径和就确定了,所以本题是一个无后效性问题(给定一个点,不管路径如何,最小路径和都固定 什么是有后效性问题?n后问题,汉诺塔问题。汉诺塔问题要求打印所有步骤的解,所以之前做过的选择必然影响后续的解),一定能改为动态规划问题。
3.回到尝试版本,看base case,找到不被依赖的位置。
base case:
if (i == matrix.length - 1 && j == matrix[0].length - 1) {
return matrix[i][j];
}
if (i == matrix.length - 1) {
return matrix[i][j] + minpath1(matrix, i, j + 1);
}
if (j == matrix[0].length - 1) {
return matrix[i][j] + minpath1(matrix, i + 1, j);
}
当i,j的位置到达最后一行或最后一列时,最小路径和是确定的。所以这张表可以填成这样:
4.分析一个普遍位置是怎么依赖的,反过来就是计算数序。
int right = minpath1(matrix, i, j + 1);// right:右边位置到右下角的最小路径和
int down = minpath1(matrix, i + 1, j);// down:下边位置到右下角的最小路径和
return matrix[i][j] + Math.min(right, down);
要知道一个普遍位置到右下角的最小路径和,就得知道它右边和下边到右下角的最小路径和。所以这张表就可以填完了:
动态规划的代码为:
public class Code_30_MinPath {
/**
* 动态规划--dp的构造从右下角到左上角 从(i,j)出发,到达最右下角位置,最短路径和是多少(返回)
* @param matrix
* @param x
* @param y
* @return
*/
private static int minpath1(int[][] matrix, int x, int y) {
int row = matrix.length;
int col = matrix[0].length;
int[][] dp = new int[row][col];
dp[row - 1][col - 1] = matrix[row - 1][col - 1];
for (int i = col - 2; i >= 0; i--) {
dp[row - 1][i] = matrix[row - 1][i] + dp[row - 1][i + 1];
}
for (int j = row - 2; j >= 0; j--) {
dp[j][col - 1] = matrix[j][col - 1] + dp[j + 1][col - 1];
}
for (int i = row - 2; i >= 0; i--) {
for (int j = col - 2; j >= 0; j--) {
dp[i][j] = matrix[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]);
}
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
System.out.print(dp[i][j] + "\t");
}
System.out.println("");
}
return dp[x][y];
}
/**
* 动态规划--dp的构造从左上角到右下角 从(0,0)出发,到达最右下角位置,最短路径和是多少(返回)
* @param m
* @return
*/
public static int minPath2(int[][] m) {
if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) {
return 0;
}
int row = m.length;// 二维数组行数
int col = m[0].length;// 二维数组列数
int[][] dp = new int[row][col];
dp[0][0] = m[0][0];// dp表
for (int i = 1; i < row; i++) {
dp[i][0] = dp[i - 1][0] + m[i][0];
}
for (int j = 1; j < col; j++) {
dp[0][j] = dp[0][j - 1] + m[0][j];
}
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + m[i][j];
}
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
System.out.print(dp[i][j] + "\t");
}
System.out.println("");
}
return dp[row - 1][col - 1];
}
/**
* 结果 1 4 9 18
9 5 8 12
14 5 11 12
22 13 15 12
12
*/
/**
* 结果:
12 11 13 14
16 8 8 5
12 7 7 1
20 12 4 0
12
*/
public static void main(String[] args) {
int[][] m = { { 1, 3, 5, 9 }, { 8, 1, 3, 4 }, { 5, 0, 6, 1 },
{ 8, 8, 4, 0 } };
System.out.println(minPath2(m));// 12
System.out.println();
System.out.println(minpath1(m, 0, 0));
}
}
原文:https://blog.csdn.net/Hank_HD/article/details/80253422