算法沉淀 —— 动态规划篇(路径问题)

简介: 算法沉淀 —— 动态规划篇(路径问题)

算法沉淀 —— 动态规划篇(路径问题)

前言

几乎所有的动态规划问题大致可分为以下5个步骤,后续所有问题分析都将基于此


  • 1.、状态表示:通常状态表示分为以下两种,其中更是第一种为主。
  • 以i为结尾,dp[i] 表示什么,通常为代求问题(具体依题目而定)
  • 以i为开始,dp[i]表示什么,通常为代求问题(具体依题目而定)
  • 2、状态转移方程
    *以上述的dp[i]意义为根据, 通过最近一步来分析和划分问题,由此来得到一个有关dp[i]的状态转移方程。
  • 3、dp表创建,初始化
  • 动态规划问题中,如果直接使用状态转移方程通常会伴随着越界访问等风险,所以一般需要初始化。而初始化最重要的两个注意事项便是:保证后续结果正确,不受初始值影响;下标的映射关系
  • 初始化一般分为以下两种:
  • 直接初始化开头的几个值。
  • 一维空间大小+1,下标从1开始;二维增加一行/一列
  • 4、填dp表、填表顺序:根据状态转移方程来确定填表顺序。
  • 5、确定返回值

一、不同路径1

【题目链接】:LCR 098. 不同路径

【题目】:

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

【分析】:

 这是个二维数组问题。我们定义dp[i][j]表示机器人走到下标为[i][j]位置时的总路径数。显然机器人要走到[i][j]位置,只能从[i][j-1]向右走、[i-1][j]向下走。所以状态转移方程为dp[i][j] = dp[i-1][j] + dp[i][j-1]。 但当i = 0或j =0时,显然状态转移方程不适应,需要特殊处理。这里我们采用的办法时,横纵都新增一行。然后我们还需将dp[0][1]或dp[1][0]初始化为1。

 接下我仅需从左往右、从上到下依次填表。最后返回结果即可!!

【代码实现】:

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));//创建dp表
        //初始化
        dp[1][0] = 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];
    }
};

二、珠宝的最高价值

【题目链接】:LCR 166. 珠宝的最高价值

【题目】:

现有一个记作二维矩阵 frame 的珠宝架,其中 frame[i][j] 为该位置珠宝的价值。拿取珠宝的规则为:

只能从架子的左上角开始拿珠宝每次可以移动到右侧或下侧的相邻位置到达珠宝架子的右下角时,停止拿取。

注意:珠宝的价值都是大于 0 的。除非这个架子上没有任何珠宝,比如 frame = [[0]]。

【分析】:

 我们可以定义dp[i][j]表示从开始到[i][j]位置所能拿到的珠宝最大价值。所以要得到dp[i][j]的值,我们只需将dp[i][j-1]和dp[i-1][j]的较大值假设当前下标[i][j]的珠宝价值即可。即动态转移方程为dp[i][j] = max(dp[i-1][j] + dp[i][j-1]) + frame[i][j]。但显然当i=0或j=0时,需要特殊处理。这里还是采用横竖都各加一行。需要注意的是此时下标的映射关系(具体参考代码)。

 ;接下我仅需从左往右、从上到下依次填表。最后返回结果!!

【代码实现】:

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {
        int m = frame.size(), n = frame[0].size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        for(int i = 1; i <= m; i++)
            for(int j = 1; j <= n; j++)
                dp[i][j] = max(dp[i][j-1], dp[i-1][j]) + frame[i - 1][j - 1];
        return dp[m][n];        
    }
};

三、下降路径最小和

【题目链接】:931. 下降路径最小和

【题目】:

给你一个 n x n 的 方形 整数数组 matrix ,请你找出并返回通过 matrix 的下降路径 的 最小和 。

下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置 (row, col) 的下一个元素应当是 (row + 1, col - 1)、(row + 1, col) 或者 (row + 1, col + 1) 。

【分析】:

 我们定义dp[i][j]表示下降到[i][j]位置时,下降路径的最小和。并且题目中已经明确表示下降到[i][j]位置有如下三种方式:

 显然我们容易得到状态转移方程为dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i-1][j+1])) + matrix[i-1][j-1]。但又有一个问题,当i=0、j=0、j=n时,状态转移方程会越界访问。所以我们给出的办法时加1行、加2列。同时为了不影响后续填表结果,我们将第一行初始为0,第1列和第n+1列初始化为INT_MAX(dp[0][1]、dp[0][n+1]除外)。

 接下来从左往右、从上到下依次填表。dp表填好后,最后一行的每个数都有可能是结果。我们需要依次比较,将最后一行的最小值返回!

【代码实现】:

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        vector<vector<int>> dp(m+1, vector<int>(n + 2, INT_MAX));
        //初始化
        for(int j = 0; j < n + 2; j++)
            dp[0][j] = 0;
        for(int i = 1; i <= m; i++)
            for(int j = 1; j <= n; j++)
            {
                dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i-1][j+1])) + matrix[i-1][j-1];
            }
        int ret = INT_MAX;
        for(int j = 1; j <= n; j++)
            ret = min(ret, dp[m][j]);
        return ret;
    }
};

四、地下城游戏

【题目链接】:174. 地下城游戏

【题目】:

恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。

有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。

为了尽快解救公主,骑士决定每次只 向右 或 向下 移动一步。

返回确保骑士能够拯救到公主所需的最低初始健康点数。

注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

【实例】:

输入:dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]]

输出:7

解释:如果骑士遵循最佳路径:右 -> 右 -> 下 -> 下 ,则骑士的初始健康点数至少为 7

【分析】:

 dp问题中我们一般定义dp[i][j]表示从开始到[i][j]位置的待解结果,即骑士从[0][0]走到[i][j]所需的最低初始健康点数。但我们发现[i][j]位置后面的数据对结果存在影响。例如:dungeon = [[1, 1],[1, -100]],假设我们走到了[0][1]位置,此时dp[0][1]=1。但此时走到dungeon[1][1]时,骑士死亡。后面结果会对当前数据有影响!!因此该思路错误。

 我们可以定义dp[i][j]表示从[i][j]位置走到结尾(假设结尾下标为[m][n])骑士所需的最低健康点数。此时示意图如下:(各位懂意思就行,手残画不了图)

 所以我们可以得到状态转移方程为dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]。但还有两个问题:

  1. 如果dungeon[i][j]非常大,此时dp[i][j]可能为负数。此时骑士死亡,不符合要求。所以我们要进一步处理,dp[i][j] = max(1, dp[i][j])(即如果dp[i][j]为负,此时表示dungeon[i][j]j较大,我们仅需保证骑士到[i][j]位置时没有死亡即可)
  2. 如果[i][j]表示结尾呢?此时状态转移方程不适应。我们给出的办法是,最后一行、和最后一列各增一行。同时为了保存新增行列对后续填dp表不产生影响,我们其中的元素初始化为INT_MAX。同时为了保证dp[m][n]在是由状态转移方程时填表正确。我们要保证的时骑士处于[m][n]位置时还剩1个健康点数即可。所以我们将dp[m+1][n]或dp[m][n+1]初始化为1!

【代码实现】:

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int m = dungeon.size(), n = dungeon[0].size();
        //创建dp表
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX));
        //初始化
        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--)
            {
                dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j];
                dp[i][j] = max(1, dp[i][j]);
            }
        return dp[0][0];
    }
};


相关文章
|
2月前
|
机器学习/深度学习 算法 C++
【DFS/回溯算法】2016年蓝桥杯真题之路径之谜详解
题目要求根据城堡北墙和西墙箭靶上的箭数,推断骑士从西北角到东南角的唯一路径。每步移动时向正北和正西各射一箭,同一格不重复经过。通过DFS回溯模拟“拔箭”过程,验证路径合法性。已知箭数约束路径唯一,最终按编号输出行走顺序。
|
3月前
|
传感器 机器学习/深度学习 编解码
MATLAB|主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性
MATLAB|主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性
223 3
|
3月前
|
机器学习/深度学习 存储 算法
动态规划算法深度解析:0-1背包问题
0-1背包问题是经典的组合优化问题,目标是在给定物品重量和价值及背包容量限制下,选取物品使得总价值最大化且每个物品仅能被选一次。该问题通常采用动态规划方法解决,通过构建二维状态表dp[i][j]记录前i个物品在容量j时的最大价值,利用状态转移方程避免重复计算子问题,从而高效求解最优解。
520 1
|
4月前
|
算法 机器人 定位技术
【VRPTW】基于matlab秃鹰算法BES求解带时间窗的骑手外卖配送路径规划问题(目标函数:最优路径成本 含服务客户数量 服务时间 载量 路径长度)(Matlab代码实现)
【VRPTW】基于matlab秃鹰算法BES求解带时间窗的骑手外卖配送路径规划问题(目标函数:最优路径成本 含服务客户数量 服务时间 载量 路径长度)(Matlab代码实现)
144 0
|
3月前
|
机器学习/深度学习 传感器 算法
【无人车路径跟踪】基于神经网络的数据驱动迭代学习控制(ILC)算法,用于具有未知模型和重复任务的非线性单输入单输出(SISO)离散时间系统的无人车的路径跟踪(Matlab代码实现)
【无人车路径跟踪】基于神经网络的数据驱动迭代学习控制(ILC)算法,用于具有未知模型和重复任务的非线性单输入单输出(SISO)离散时间系统的无人车的路径跟踪(Matlab代码实现)
234 2
|
3月前
|
机器学习/深度学习 传感器 算法
基于全局路径的无人地面车辆的横向避让路径规划研究[蚂蚁算法求解](Matlab代码实现)
基于全局路径的无人地面车辆的横向避让路径规划研究[蚂蚁算法求解](Matlab代码实现)
220 8
|
3月前
|
算法 数据挖掘 区块链
基于遗传算法的多式联运车辆路径网络优优化研究(Matlab代码实现)
基于遗传算法的多式联运车辆路径网络优优化研究(Matlab代码实现)
126 2
|
3月前
|
存储 算法 数据可视化
基于禁忌搜索算法的TSP问题最优路径搜索matlab仿真
本程序基于禁忌搜索算法解决旅行商问题(TSP),旨在寻找访问多个城市的最短路径。使用 MATLAB 2022A 编写,包含城市坐标生成、路径优化及结果可视化功能。通过禁忌列表、禁忌长度与藐视准则等机制,提升搜索效率与解的质量,适用于物流配送、路径规划等场景。
|
3月前
|
机器学习/深度学习 负载均衡 算法
【卡车和无人机协同配送路径优化】遗传算法求解利用一辆卡车和两架无人机配合,将小包裹递送给随机分布的客户,以使所有站点都由卡车或无人机递送一次后返回起始位置(中转站)研究(Matlab代码实现)
【卡车和无人机协同配送路径优化】遗传算法求解利用一辆卡车和两架无人机配合,将小包裹递送给随机分布的客户,以使所有站点都由卡车或无人机递送一次后返回起始位置(中转站)研究(Matlab代码实现)
272 7
|
3月前
|
机器学习/深度学习 传感器 算法
基于matlab瞬态三角哈里斯鹰算法TTHHO多无人机协同集群避障路径规划(目标函数:最低成本:路径、高度、威胁、转角)(Matlab代码实现)
基于matlab瞬态三角哈里斯鹰算法TTHHO多无人机协同集群避障路径规划(目标函数:最低成本:路径、高度、威胁、转角)(Matlab代码实现)
154 1