• 递归和动态规划——矩阵的最小路径和


    给你一个二维数组, 二维数组中的每个数都是正数, 要求从左上角走到右下角, 每一步只能向右或者向下。 沿途经过的数字要累加起来。 返回最小的路径和。

    解:
    暴力递归
    * 因为是从左上角到右下角,只能向右或者向下,
    * 可以使用递归,
    * 把问题简化为
    * 当前位置(i, j)和右边位置(i + 1, j)和下面位置(i, j + 1)之间的问题

    * base case:
    * 当i == row - 1 && j == col - 1时,位于矩阵右下角,即为最终结果
    * 当i == row - 1时,位于最后一行,此时只能向右走
    * 当j == col - 1时,位于最右一行,此时只能向下走

    * 当位于矩阵中间某个位置时(i, j),
    * 此时要判断右边位置(i + 1, j)和下面位置(i, j + 1)中的值谁大谁小

    动态规划
    * 是从结果倒推回去推初始状态
    * 1.dp数组的维度与大小:递归中的可变参数是 i, j 两个,所以dp数组是二维的 dp[matrix.length][matrix[0].length]
    * 2.dp中需要位置为 初始位置(0, 0)
    * 3.利用base case把不被依赖的位置标出来:
    * 右下角位置
    * 4.根据下面的递归调用分析普遍位置中的依赖关系:
    * 最下面一行(row - 1, j)依赖的位置是(row - 1, j + 1)
    * 最右边一列(i, col - 1)依赖的位置是(i + 1, col - 1)
    * 当前位置(i, j)依赖的位置有(i + 1, j), (i, j + 1)

    暴力递归版本:

    //正着递归
        public static int minPath1(int[][] matrix){
            if(matrix.length <= 0 || matrix[0].length <= 0 || matrix == null) return 0;
            return path(matrix, 0, 0);
        }
        public static int path(int[][] matrix, int i, int j){
            if(i == matrix.length - 1 && j == matrix[0].length - 1) return matrix[i][j];
            if(i == matrix.length - 1) return matrix[i][j] + path(matrix, i, j + 1);
            if(j == matrix[0].length - 1) return matrix[i][j] + path(matrix, i + 1, j);
    
            return matrix[i][j] + Math.min(path(matrix, i + 1, j),
                    path(matrix, i, j + 1));
        }
    

      

    其实还可以倒着递归

    改为动态规划版本:

    正着推

    不被依赖的位置是(0, 0),

    普通位置的依赖关系:(i, j) 依赖它左边(i, j - 1)和上边(i - 1, j)位置

    最终结果是(row - 1, col - 1)

    //正着推
        public static int dpMinPath1(int[][] matrix){
            if(matrix.length == 0 || matrix[0].length == 0 || matrix == null) return 0;
            int row = matrix.length;
            int col = matrix[0].length;
    
            int[][] dp = new int[row][col];
            //不被依赖的位置
            dp[0][0] = matrix[0][0];
            //最左一列,只能从上面往下面走
            for(int i = 1; i < row; i++){
                dp[i][0] = dp[i - 1][0] + matrix[i][0];
            }
            //最上面一行,只能从左边往右边走
            for(int i = 1; i < col; i++){
                dp[0][i] = dp[0][i - 1] + matrix[0][i];
            }
    
            //普通的位置(i, j),可以从左边到达(i, j - 1),也可以从上边到达(i - 1, j)
            for(int i = 1; i < row; i++){
                for(int j = 1; j < col; j++){
                    dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + matrix[i][j];
                }
            }
            return dp[row - 1][col - 1];
        }
    

      

    还可以倒着推

    不被依赖的位置(row - 1, col - 1)

    普通位置的依赖关系:(i, j)依赖它右边(i, j + 1)和下边(i + 1, j)的位置

    最终结果是(0, 0)位置的值

    //倒着推
        public static int dpMinPath2(int[][] matrix){
            if(matrix.length == 0 || matrix[0].length == 0 || matrix == null) return 0;
            int row = matrix.length;
            int col = matrix[0].length;
            //dp数组,用来记录各个位置的状态
            int[][] dp = new int[row][col];
            //不被依赖的位置
            dp[row - 1][col - 1] = matrix[row - 1][col - 1];
            //最右边的一列
            for(int i = row - 2; i >= 0; i--){
                dp[i][col - 1] = dp[i + 1][col - 1] + matrix[i][col - 1];
                System.out.println("dp["+ i +", "+ (col - 1) + "] " + dp[i][col - 1]);
            }
            System.out.println();
            //最下面一行
            for(int i = col - 2; i >= 0; i--){
                dp[row - 1][i] = dp[row - 1][i + 1] + matrix[row - 1][i];
                System.out.println("dp["+ (row - 1) +", "+ i + "] " + dp[row - 1][i]);
            }
            //普通位置的值
            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]);
                }
            }
            return dp[0][0];
        }
    

      

     测试代码

    生成随机矩阵

    public static int[][] generateRandomMatrix(int row, int col){
            if(row <= 0 || col <= 0) return null;
    
            int[][] matrix = new int[row][col];
            for(int i = 0; i < row; i++){
                for(int j = 0; j < col; j++){
                    matrix[i][j] = (int) (Math.random()*10);
                }
            }
            return matrix;
        }
    

      

    测试代码:

    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(minPath1(m));
            System.out.println(dpMinPath1(m));
            System.out.println(dpMinPath2(m));
    
            m = generateRandomMatrix(6, 7);
            System.out.println(minPath1(m));
            System.out.println(dpMinPath1(m));
            System.out.println(dpMinPath2(m));
        }
    

      

  • 相关阅读:
    .NET重构(七):VS报表的制作
    【Linq】标准查询操作符
    1 TaskQueue 实现Task 队列
    1 疑惑处理
    1 JSONP
    1 Web 知识基础
    20 闭包
    1 基础知识
    Web 常用
    【Winform】2 Button
  • 原文地址:https://www.cnblogs.com/SkyeAngel/p/8965822.html
Copyright © 2020-2023  润新知