动规训练2

一、最小路径和

1、题目解析

就是一个人从左上往做下走,每次只能往右或者往下,求他到终点时,路径上数字和最小,返回最小值

2、算法原理

a状态表示方程

小技巧:经验+题目要求

用一个二维数组表示,创建一个二维数组

dp[i][j]  表示当前从起点到当前节点的最小值

b状态转移方程

一个小技巧:找到最近的一步,划分问题

能到达dp[i][j]的只有一条路径,上或者左边,选择一个两者较小的值。因为dp[i-1][j]或者dp[i][j-1]也是表示他那个节点对路径最小值——问题进入闭环

dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j]

c初始化

以下面这张图为例,为了避免越界访问我们本来应该在1,1的位置开始遍历的。给第一行和第一列进行初始化。

为了简化这个过程,我们给它多创建一行多创建一列,再给里面赋上合适的值这样避免了越界访问,也避免了初始化繁琐的操作,我们可以直接进行填表。

既然要选取较小值,那我们就直接弄上最大值INT_MAX,然后在0,1和1,0的位置附上0就行了,这样dp[1][1]就能保证刚好就是grid[0][0],这样从1开始的每一列都是获取上面的路径和,每一行都是获取左边的路径和,这样就是合理的赋值。

这其实就是运用了虚拟节点来进行初始化,简化初始化的行为。

这种方法需要注意两点:

  1. 进行合理赋值,保证后面填表的值是正确的
  2. 注意取值的下标映射

因为我们的dp表是多了一行多了一列,我们需要i-1,j-1才能取到正确的值

d填表顺序

从左到右,从上到下

e返回值

dp[m][n]

3、代码

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
            //建dp表
            //初始化
            //填表
            //返回值
            int m=grid.size();
            int n=grid[0].size();
            vector<vector<int>> dp(m+1,vector<int>(n+1,INT_MAX));
            dp[0][1]=0;
            dp[1][0]=0;
            for(int i=1;i<=m;i++){
                for(int j=1;j<=n;j++){
                    dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i-1][j-1];
                }
            }
            return dp[m][n];
    }
};

二、地下城游戏

1、题目解析

 这道题相较于第一题多了负数,但是还是有可以一样的处理——进行相加,并且还多了一个限制条件,骑士的值不能为零。也是只能向下和向右走。不过这道题需要返回骑士需要救到公主所需最低的健康值。

2、算法原理

a状态表示方程

经验+题目要求

有两种解题方式:

1、以某个位置为结尾。。。。。

假设dp[i][j]表示从期待您出发到达i,j位置数所需的最低健康点数

这个思路其实是错误的,因为你不仅需要进入这个房间,还要静茹下一个房间,这就导致了你不能简单的通过这个房间的前一步进行判断,还需要考虑后续房间的影响。————有后效性

2、以某个位置为起点。。。。。

dp[i][j]表示从这李出发到达终点所需最低初始健康。

b状态转移方程

根据最近的一步,划分问题

假设进入d[i][j]这个房间所需最小生命值为x

其实想要右边房间的话就需要:x+d[i][j]>=dp[i][j+1]

x>=dp[i][j+1]-d[i][j]

还有一种特殊情况需要我们考虑如果房间里面是一个较大血包的话,dp[i][j+1]-d[i][j]是有可能小于0的————这表示你就算是负多少多少都能够走到终点,但这是不合理的,负数骑士都死掉了。

所以

c初始化

虚拟节点(处理边界问题的好方法)

这道题需要考虑边界问题是最后一行和最后一列。

需要注意的两点:

1、设置合适的值,保证填表时值的正确性

走出迷宫最少得剩下一滴血

2、注意下标映射

因为是添加在最后一行和最后一列,不需要考虑下标问题

d填表顺序

从下往上,从右往左

e返回值

dp[0][0]

3、代码

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        //建表
        //初始化
        //填表
        //返回值
        int m=dungeon.size(),n=dungeon[0].size();
        vector<vector<int>> dp(m+1,vector<int> (n+1,INT_MAX));
        dp[m][n-1]=1;
        dp[m-1][n]=1;
        for(int i=m-1;i>=0;i--)
        {
            for(int j=n-1;j>=0;j--){
                int t=min(dp[i+1][j],dp[i][j+1])-dungeon[i][j];
                dp[i][j]=max(1,t);
            }
        }
        return dp[0][0];
    }
};

  • 21
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一周学八天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值