动态规划--矩阵最小的路径和
2017-05-27 20:03
721 查看
题目描述:给定一个 N*M 的矩阵arr,从左上角开始每次只能向下或者向右走,最后到达右下角。路径上所有点的数字和为 路径和,求最小的路径和。
典型的动态规划。状态方程为: dp[i][j] = getMin( dp[i - 1][j] ,dp[i][j - 1] ) + arr[i][i] 。dp[i][j] 表示 达到点 arr[i][j] 是的最小路径和,因为每次只能向下或者向右,所以要达到 arr[i][j] 必须先经过 arr[i - 1][j - 1] 或者 arr[i][j - 1] 其中一个点,找出路径最小的即可。
因为每次只能向下和向右,所以第一行只能从左一直往右走,而第一列只能从上一直往下走,并且将经过的点累加起来。
所以我们可以先对第一行、第一列的 dp[i][j] 进行初始化。
具体代码:
/*******************2017-5-31 修改********************/
上面的方法中,时间复杂度为 O(n*m), 但是辅助空间也为 O(n*m)。通过压缩空间的方法来减小辅助空间,使得辅助空间只需要 O(min(n,m)),
状态转移方程为: dp[ i ] = min ( dp[ i - 1] , dp[ i ] ) + arr[ i ] [ j ] .
我们用一个实际的例子来分析说明,比如现在有一个 5*4 的矩阵:
1 1 1 1
2 3 5 4
1 7 2 3
1 3 3 4
2 3 4 1
明显 n > m,我们设置一个辅助的数组 int[ ] dp = new int [ m ]。dp[ 0 ] = arr[ 0 ][ 0 ];
我们从第一行开始(如果是 m > n,那么就从 第一列开始),因为第一行只能从左到右(第一列则只能从上到下),
所以我们初始化 dp[ 1 ] = 2,dp[ 2 ] = 3, dp[ 3 ] = 4。接下来看第二行,因为第二行的第一个只能是从上到下,所以此时应该为 1 + 2 = 3,即 dp[ 0 ] =
dp[ 0 ] + arr[ 1 ][ 0 ] = 3, 而第二行的第二个,有两种方法可以到达,一种是从左边的2 到达,一种是从上面的 1 到达,我们要的是更小的路径,所以我们选择左边跟上边中更小的那个即:
min( dp[ i - 1],dp[ i ]
) 。式子中红色 的 dp[ i ] ,表示的是我们能到达上一行的 第 i 个数的 最小路径值,也就是我们当前这一行的第 i 的数上面数值,跟当前行左边的数值进行比较取更小的 。最终我们遍历到最后一行最后一个数字,得出的dp [ m - 1 ] 就是最小的路径。
具体的代码如下:
//压缩空间的方法,只需要额外的 O(min(n,m)) 空间 ,代码过于冗余,精益求精!再优化
public static int minPath2(int[][] arr,int n,int m){
if(arr == null)
return 0;
if(n <= m){
int[] dp = new int
;
dp[0] = arr[0][0];
for(int i = 1; i < n; i++){
dp[i] = dp[i - 1] + arr[i][0];
}
for(int i = 1; i < m; i++){
dp[0] = dp[0] + arr[0][i];
for(int j = 1; j < n; j++){
dp[j] = Math.min(dp[j], dp[j - 1]) + arr[j][i];
}
}
return dp[n - 1];
}
else{
int[] dp = new int[m];
dp[0] = arr[0][0];
for(int i = 1; i < m; i++){
dp[i] = dp[i - 1] + arr[0][i];
}
for(int i = 1; i < n; i++){
dp[0] = dp[0] + arr[i][0];
for(int j = 1; j < m; j++){
dp[j] = Math.min(dp[j], dp[j - 1]) + arr[i][j];
}
}
return dp[m - 1];
}
}
//根据上面的 minPath2 方法进行优化,参考了《程序员代码面试指南》
public static int minPath3(int[][] arr,int n,int m){
if(arr == null)
return 0;
//比较行数 和 列数大小
int more = Math.max(n, m);
int less = Math.min(n, m);
boolean rowmore = (more == n); //标志变量,行数是否大于列数
int[] dp = new int[less];
dp[0] = arr[0][0];
for(int i = 1; i < less; i++){
dp[i] = dp[i - 1] + (rowmore ? arr[0][i] : arr[i][0]); //如果行数大于列数,则加上arr[0][i]
}
for(int i = 1; i < more; i++){
dp[0] = dp[0] + (rowmore ? arr[i][0] : arr[0][i]);
for(int j = 1; j < less; j++){
dp[j] = Math.min(dp[j - 1], dp[j]) + (rowmore ? arr[i][j] : arr[j][i]);
}
}
return dp[less - 1];
}
minPath2 的方法容易理解,但是代码太冗余
minPath3 方法的精髓在于设置了一个标志标量 rowmore ,用于选择我们应该先遍历每一行还是先遍历每一列,细细体会。
典型的动态规划。状态方程为: dp[i][j] = getMin( dp[i - 1][j] ,dp[i][j - 1] ) + arr[i][i] 。dp[i][j] 表示 达到点 arr[i][j] 是的最小路径和,因为每次只能向下或者向右,所以要达到 arr[i][j] 必须先经过 arr[i - 1][j - 1] 或者 arr[i][j - 1] 其中一个点,找出路径最小的即可。
因为每次只能向下和向右,所以第一行只能从左一直往右走,而第一列只能从上一直往下走,并且将经过的点累加起来。
所以我们可以先对第一行、第一列的 dp[i][j] 进行初始化。
具体代码:
import java.util.Scanner; /** * 输入一个 N*M 的矩阵,从左上角开始,每次只能向下或者向右走,最后到达右下角的位置 * 将路径上所以数字加起来就是路径和,求最小路径和 * * @author luzi * */ public class minPathSumOfArr { public static void main(String args[]){ Scanner scan = new Scanner(System.in); while(scan.hasNext()){ int n = scan.nextInt(); //行数 int m = scan.nextInt(); //列数 int[][] arr = new int [m]; for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++){ arr[i][j] = scan.nextInt(); } } System.out.println(minPath(arr,n,m)); } } public static int minPath(int[][] arr,int n,int m){ if(arr == null) return 0; int[][] dp = new int [m]; dp[0][0] = arr[0][0]; for(int i = 1; i < n;i++){ dp[i][0] = dp[i - 1][0] + arr[i][0]; } for(int i = 1; i < m; i++){ dp[0][i] = dp[0][i - 1] + arr[0][i]; } for(int i = 1; i < n; i++){ for(int j = 1; j < m; j++){ dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + arr[i][j]; } } return dp[n - 1][m - 1]; } }
/*******************2017-5-31 修改********************/
上面的方法中,时间复杂度为 O(n*m), 但是辅助空间也为 O(n*m)。通过压缩空间的方法来减小辅助空间,使得辅助空间只需要 O(min(n,m)),
状态转移方程为: dp[ i ] = min ( dp[ i - 1] , dp[ i ] ) + arr[ i ] [ j ] .
我们用一个实际的例子来分析说明,比如现在有一个 5*4 的矩阵:
1 1 1 1
2 3 5 4
1 7 2 3
1 3 3 4
2 3 4 1
明显 n > m,我们设置一个辅助的数组 int[ ] dp = new int [ m ]。dp[ 0 ] = arr[ 0 ][ 0 ];
我们从第一行开始(如果是 m > n,那么就从 第一列开始),因为第一行只能从左到右(第一列则只能从上到下),
所以我们初始化 dp[ 1 ] = 2,dp[ 2 ] = 3, dp[ 3 ] = 4。接下来看第二行,因为第二行的第一个只能是从上到下,所以此时应该为 1 + 2 = 3,即 dp[ 0 ] =
dp[ 0 ] + arr[ 1 ][ 0 ] = 3, 而第二行的第二个,有两种方法可以到达,一种是从左边的2 到达,一种是从上面的 1 到达,我们要的是更小的路径,所以我们选择左边跟上边中更小的那个即:
min( dp[ i - 1],dp[ i ]
) 。式子中红色 的 dp[ i ] ,表示的是我们能到达上一行的 第 i 个数的 最小路径值,也就是我们当前这一行的第 i 的数上面数值,跟当前行左边的数值进行比较取更小的 。最终我们遍历到最后一行最后一个数字,得出的dp [ m - 1 ] 就是最小的路径。
具体的代码如下:
//压缩空间的方法,只需要额外的 O(min(n,m)) 空间 ,代码过于冗余,精益求精!再优化
public static int minPath2(int[][] arr,int n,int m){
if(arr == null)
return 0;
if(n <= m){
int[] dp = new int
;
dp[0] = arr[0][0];
for(int i = 1; i < n; i++){
dp[i] = dp[i - 1] + arr[i][0];
}
for(int i = 1; i < m; i++){
dp[0] = dp[0] + arr[0][i];
for(int j = 1; j < n; j++){
dp[j] = Math.min(dp[j], dp[j - 1]) + arr[j][i];
}
}
return dp[n - 1];
}
else{
int[] dp = new int[m];
dp[0] = arr[0][0];
for(int i = 1; i < m; i++){
dp[i] = dp[i - 1] + arr[0][i];
}
for(int i = 1; i < n; i++){
dp[0] = dp[0] + arr[i][0];
for(int j = 1; j < m; j++){
dp[j] = Math.min(dp[j], dp[j - 1]) + arr[i][j];
}
}
return dp[m - 1];
}
}
//根据上面的 minPath2 方法进行优化,参考了《程序员代码面试指南》
public static int minPath3(int[][] arr,int n,int m){
if(arr == null)
return 0;
//比较行数 和 列数大小
int more = Math.max(n, m);
int less = Math.min(n, m);
boolean rowmore = (more == n); //标志变量,行数是否大于列数
int[] dp = new int[less];
dp[0] = arr[0][0];
for(int i = 1; i < less; i++){
dp[i] = dp[i - 1] + (rowmore ? arr[0][i] : arr[i][0]); //如果行数大于列数,则加上arr[0][i]
}
for(int i = 1; i < more; i++){
dp[0] = dp[0] + (rowmore ? arr[i][0] : arr[0][i]);
for(int j = 1; j < less; j++){
dp[j] = Math.min(dp[j - 1], dp[j]) + (rowmore ? arr[i][j] : arr[j][i]);
}
}
return dp[less - 1];
}
minPath2 的方法容易理解,但是代码太冗余
minPath3 方法的精髓在于设置了一个标志标量 rowmore ,用于选择我们应该先遍历每一行还是先遍历每一列,细细体会。
相关文章推荐
- 矩阵的最小路径和二维动态规划的空间压缩
- 动态规划6:台阶问题和矩阵最小路径问题
- 动态规划3:矩阵最小路径和问题
- 动态规划之矩阵的最小路径和
- 动态规划 最小路径和
- LeetCode 在矩阵中寻找路径类的动态规划问题
- 动态规划之最大矩阵路径
- java动态规划最小路径和
- 动态规划求解矩阵累计和最大的路径
- 动态规划 - 矩阵连乘问题
- 动态规划--寻找最佳矩阵乘法次序
- 动态规划——矩阵连乘问题
- 线性规划与网络流24题——03最小路径覆盖问题
- 动态规划——最小投资 小程序
- 动态规划——矩阵连乘的问题
- 动态规划之矩阵连乘
- 动态规划解决矩阵左上角到右下角距离最大(转 http://blog.csdn.net/ycc892009/article/details/6523167)
- 动态规划之矩阵链乘
- 动态规划解——有向图中的最长路径
- 动态规划——矩阵连乘的问题