动态规划算法专题(二):路径问题

1. 不同路径

. - 力扣(LeetCode)

1.1 算法原理 

  • 状态表示dp[i][j]:走到(i,j)位置,一共有多少种方法(以(i,j)位置为结尾)
  • 状态转移方程:dp[i][j]=dp[i-1][j]+dp[i][j-1];
  • 初始化:dp[0][1]=1;
  • 建表顺序:从上往下的每一行填表,每一行中从左往右。
  • 返回值:dp[m][n]

1.2 算法代码

class Solution {
    //1.创建dp表
    //2.初始化
    //3.填表顺序
    //4.返回值
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m + 1][n + 1];
        //初始化
        dp[0][1] = 1;
        for(int i  = 1; i <= m; i++) {
            for(int j = 1; j <= n; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m][n];
    }
}

2. 不同路径 II

. - 力扣(LeetCode)

2.1 算法原理

本题算法思想和上题基本一致,但需额外注意以下几点:

  1. dp填表时,需要注意当前位置(i,j)是否存在障碍物,若存在障碍物则dp[i][j]=0;
  2. 初始化后,虚拟节点值的设置与上题相同。但,由于本题是数组形式,所以需要额外注意原数组与dp表的下标映射关系。

2.2 算法代码

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        //以(i,j)位置为终点,到达(i,j)位置的路径总数
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m + 1][n + 1];
        dp[0][1] = 1;
        for(int i = 1; i <= m; i++) {
            for(int j = 1; j <= n; j++) {
                if(obstacleGrid[i - 1][j - 1] != 1) 
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m][n];
    }
}

3. 珠宝的最高价值

. - 力扣(LeetCode)

3.1 算法原理 

  • dp[i][j]状态表示:到达(i,j)位置,能得到的最高价值(以(i,j)位置为结尾)
  • 状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + arr[i - 1][j - 1];
  • 初始化:  dp=new int[m+1][n+1];
  • 虚拟节点值为0(不影响后续填表)
  • 下标映射  dp(i,j)-->arr(i-1,j-1)
  • 返回值:dp[m][n]

 3.2 算法代码

class Solution {
    public int jewelleryValue(int[][] frame) {
        //dp[i][j]:以(i,j)位置为结尾,到达(i,j)位置得到的最高价值数
        int m = frame.length;
        int n = frame[0].length;
        int[][] dp = new int[m + 1][n + 1];
        for(int i = 1; i <= m; i++) {
            for(int j = 1; j <= n; j++) {
                //根据最近的一步划分问题 --> 状态转移方程
                int val = Math.max(dp[i - 1][j], dp[i][j - 1]) + frame[i - 1][j - 1];
                dp[i][j] = val;
            }
        }
        return dp[m][n];
    }
}

4. 下降路径最小和

. - 力扣(LeetCode)

4.1 算法原理 

  • 状态表示dp[i][j]:达到(i,j)位置,最小下降路径(以(i,j)位置为结尾)
  • 状态转移方程:dp[i][j]=min(dp[左斜上],dp[正上],dp[右斜上])+arr[i-1][j-1];
  • 初始化:dp表,多创建一行,多创建两列。
  • 虚拟节点的初始值要保证后续填表的正确性:第一行 -> 0; 第一列、最后一列 -> MAX_V
  • 下标映射
  • 建表顺序:从上往下
  • 返回值:dp表中最后一行的最小值

4.2 算法代码

class Solution {
    public int minFallingPathSum(int[][] matrix) {
        int n = matrix.length;
        int[][] dp = new int[n + 1][n + 2];
        for(int i = 0; i < n + 1; i++) Arrays.fill(dp[i], Integer.MAX_VALUE);
        //初始化
        for(int i = 0; i < n + 2; i++) dp[0][i] = 0;
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                int x1 = Math.min(Math.min(dp[i - 1][j - 1], dp[i - 1][j]), dp[i - 1][j + 1]);
                dp[i][j] = x1 + matrix[i - 1][j - 1];
            }
        }
        int ret = Integer.MAX_VALUE;
        for(int i = 1; i <= n; i++) ret = Math.min(ret, dp[n][i]);
        return ret;
    }
}

5. 最小路径和

. - 力扣(LeetCode)

5.2 算法原理

  • 状态表示dp[i][j]:从起点开始,到达(i,j)位置的最小路径和(以(i,j)位置为结尾)
  • 状态转移方程:dp[i][j]=min(dp[i-1][j], dp[i][j-1])+arr[i-1][j-1];
  • 建表顺序:从上往下,从左往右

 5.3 算法代码

class Solution {
    public int minPathSum(int[][] grid) {
        //1. 建dp表
        //2. 初始化
        //3. 填表顺序
        //4. 返回值
        int m = grid.length, n = grid[0].length;
        int[][] dp = new int[m + 1][n + 1];
        //初始化
        for(int i = 0; i <= m; i++) Arrays.fill(dp[i], Integer.MAX_VALUE);
        dp[1][0] = 0; dp[0][1] = 0;
        for(int i = 1; i <= m; i++) {
            for(int j = 1; j <= n; j++) {
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i - 1][j - 1];
            }
        }
        return dp[m][n];
    }
}

6. 地下城游戏

. - 力扣(LeetCode)

6.1 算法原理

状态表示dp[i][j]:

  1.  以(i,j)位置为结尾,到达(i,j)位置所需的最低生命值(dp[i][j]的值与后面路径的dp值有关,故该方法不可行)
  2. 以(i,j)位置为起点,到达终点所需的最低生命值(可行)

状态转移方程:

  1. dp[i][j]=min(dp[i+1][j], dp[i][j+1]) - arr[i][j];(从(i,j)位置走出时的生命值,要大于等于进入下一位置所需的生命值,x+arr[i][j]>=dp[i+1][j](或者x+arr[i][j]>=dp[i][j+1]) --> x>=dp[i+1][j]-arr[i][j])
  2. dp[i][j] = max(1, dp[i][j]);//本位置能够增加的生命值过多,导致计算出的值为负数,此时初始生命值可以为1

6.2 算法代码

class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        int m = dungeon.length, n = dungeon[0].length;
        int[][] dp = new int[m + 1][n + 1];
        for(int i = 0; i <= m; i++) dp[i][n] = Integer.MAX_VALUE;
        for(int j = 0; j <= n; j++) dp[m][j] = Integer.MAX_VALUE;
        //骑士拯救公主后生命值最少为1
        dp[m][n - 1] = dp[m - 1][n] = 1;
        for(int i = m - 1; i >= 0; i--) {
            for(int j = n - 1; j >= 0; j--) {
                int x = Math.min(dp[i + 1][j], dp[i][j + 1]);
                //当dungeon[i][j]为过大正数时,x - dungeon[i][j]计算得到的是负数
                //此时骑士的初始值可以为1
                dp[i][j] = Math.max(1, x - dungeon[i][j]);
            }
        } 
        return dp[0][0];
    }
}

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值