动态规划求解的两个条件:
1)最优解问题
2)大问题可以拆分成小问题,大问题的最优解包含小问题的最优解,将小问题的最优解保存起来,在求大问题最优解的时候无需重新求解,直接拿来用即可。
具体问题
需求一:
给定m*n矩阵,从左上角出发,到右下角,每次只能向右走或者向下走,求共有多少路径?
分析:
假设路径数是f(m, n),那么第一步如果向右走,那么有f(m, n-1)种走法,如果第一步向下走,那么有f(m-1, n)种走法,所以可以得到递推式:f(m, n) = f(m, n-1) + f(m-1, n)。这样就可以使用DP求解。并且,如果m或n为1,即只有一行或者只有一列,那么共有一条路径。可以创建int[m+1][n+1]数组,给只有一行/一列的元素置1,然后递推求解。
代码:
class Solution {
public int uniquePaths(int m, int n) {
//第一步向右走或者向下走,如果向右走,就是f(m, n-1),向下走,还有f(m-1, n)种情况
//所以f(m, n) = f(m, n-1)+f(m-1, n)
//可以使用动态规划,设置二维数组
int[][] dp = new int[m+1][n+1];
//如果m==1或者n==1,那么就一种情况
for(int i = 1; i <= m; i++)
dp[i][1] = 1;
for(int i = 1; i <= n; i++)
dp[1][i] = 1;
for(int i = 2; i <= m; i++){
for(int j = 2; j <= n; j++)
dp[i][j] = dp[i-1][j]+dp[i][j-1];
}
return dp[m][n];
}
}
需求二:
给定二维数组m*n,每个元素的值表示距离,求从[0][0]到[m-1][n-1]的最短距离,每次只能向右走或者向下走。
分析:
动态规划的问题。我们可以正向求解,求出起点到每个点的最短距离,也可以反向求解,求出每个节点到终点的最短距离。
1)正向求解
对于第一个点,距离就是它的值。对于第一行的点,到起点的最短距离就是当前点的值加上左边点到起点的最短距离。对于第一列的点,到起点的最短距离就是当前点到上一个点到起点的最短距离。对于其它的点,要么是从上面的点过来,要么是从下面的点过来,所以求二者的最小值,加上当前点的值即可。最后返回终点到起点的距离即可。
2)反向求解
对于最后一个点,距离就是它的值。对于最后一行的点,到终点的最短距离就是当前值加上右边点到终点的最短距离。对于最后一列的点,到终点的距离就是当前值加上其下面一个点到终点最短距离。对于其它的点,要么是向右走,要么向下走,选择二者的较小值加上当前值即为最短距离。最后返回起点到终点的距离。
3)原始数组是否可以改变?
如果可以改变,那么可以直接在原始数组上进行修改,如果正向求解,那么修改后的数组中的值就是到起点的最短距离,如果是反向求解,那么修改后的数组中的值就是到终点的最短距离。
如果不可以改变,那么需要创建和原始数组同大小的数组,正向求解和反向求解的结果同上。
代码:
class Solution {
public int minPathSum(int[][] grid) {
//异常处理
if(grid == null || grid.length == 0)
return 0;
//思路一:修改数组grid,正向求解
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(i == 0 && j == 0)
grid[i][j] = grid[i][j];
else if(i == 0 && j != 0)
grid[i][j] += grid[i][j-1];
else if(i != 0 && j == 0)
grid[i][j] += grid[i-1][j];
else
grid[i][j] += Math.min(grid[i-1][j], grid[i][j-1]);
}
}
return grid[grid.length-1][grid[0].length-1];
/*
//思路二:修改数组,反向求解
for(int i = grid.length-1; i >= 0 ; i--){
for(int j = grid[0].length-1; j >= 0; j--){
if(i == grid.length-1 && j == grid[0].length-1)
grid[i][j] = grid[i][j];
else if(i != grid.length-1 && j == grid[0].length-1)
grid[i][j] += grid[i+1][j];
else if(i == grid.length-1 && j != grid[0].length-1)
grid[i][j] += grid[i][j+1];
else
grid[i][j] += Math.min(grid[i+1][j], grid[i][j+1]);
}
}
return grid[0][0];
*/
/*
//思路三:开辟新的数组,正向求解
int row = grid.length, col = grid[0].length;
int[][] dp = new int[row][col];
for(int i = row-1; i >= 0; i--){
for(int j = col-1; j >= 0; j--){
if(i == row-1 && j == col-1)
dp[i][j] = grid[i][j];//表示最后一个元素到最后一个元素的最短距离
else if(i == row-1 && j != col-1)
dp[i][j] = grid[i][j] + dp[i][j+1];
else if(i != row-1 && j == col-1)
dp[i][j] = grid[i][j] + dp[i+1][j];
else
dp[i][j] = grid[i][j] + Math.min(dp[i+1][j], dp[i][j+1]);
}
}
return dp[0][0];
*/
/*
//思路四:开辟新的数组,正向求解
int row = grid.length, col = grid[0].length;
int[][] dp = new int[row][col];
for(int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
if(i == 0 && j == 0)
dp[i][j] = grid[i][j];
else if(i == 0 && j != 0)
dp[i][j] = grid[i][j]+dp[i][j-1];
else if(i != 0 && j == 0)
dp[i][j] = grid[i][j]+dp[i-1][j];
else
dp[i][j] = grid[i][j]+Math.min(dp[i-1][j], dp[i][j-1]);
}
}
return dp[row-1][col-1];
*/
}
}