当前位置: 首页 > news >正文

算法导论(动态规划)——路径问题

算法思路(62)

  1. 状态表示
    在解决“路径类”问题时,常见的状态表示形式有两种:

    • 形式一:从位置 [i,j] 出发的路径计数。
    • 形式二:从起始位置到达位置 [i,j] 的路径计数。

    本文选择第二种形式来定义状态:设 dp[i][j] 表示到达位置 [i,j] 的路径总数。

  2. 状态转移方程
    根据状态的定义,若 dp[i][j] 表示到达位置 [i,j] 的路径数,考虑到达该位置的方式,可以得到以下两种可能情况:

    • 从上方位置 [i−1,j] 向下移动一格;
    • 从左方位置 [i,j−1] 向右移动一格。

    因此,状态转移方程可表示为:

    dp[i][j]=dp[i−1][j]+dp[i][j−1]
  3. 初始化
    为了便于后续的状态填表,建议在状态表的前面添加一行和一列作为辅助节点。这里有两个关键点需注意:

    • 值的设置:确保辅助节点的值正确,以保证后续的填表结果正确。
    • 下标映射:添加行列后,实际填表时需注意对应的下标映射。

    在本题中,我们可以在状态表中插入一行和一列,并将 dp[0][1] 初始化为 1,以确保后续填表过程顺利进行。

  4. 填表顺序
    根据状态转移方程的推导,填表顺序应为:

    • 从上到下逐行填充;
    • 在填充每一行时,从左到右逐列填充。
  5. 返回值
    最终结果即为到达位置 [m,n] 的路径数量,返回 dp[m][n] 的值即可。

C++:

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

Java:

class Solution 
{
 public int uniquePaths(int m, int n) 
 {
 // 1. 创建 dp 表 
 // 2. 初始化 
 // 3. 填表 
 // 4. 返回值 
 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];
 }
}

算法思路(63)

本题为不同路径问题的变种,需考虑障碍物的影响,稍加修改即可求解。以下是详细的算法思路:

  1. 状态表示
    在解决“路径类”问题时,常见的状态表示形式有两种:

    • 形式一:从位置 [i,j] 出发的路径数量。
    • 形式二:从起始位置到达位置 [i,j] 的路径数量。

    本文采用第二种形式来定义状态:令 dp[i][j] 表示到达位置 [i,j] 的路径总数。

  2. 状态转移
    通过分析可得,若dp[i][j]表示达到位置[i,j]的路径数量,则可通过以下两种方式到达该位置:

    • 从上方位置 [i−1,j] 向下移动;
    • 从左侧位置 [i,j−1] 向右移动。

    然而,需注意如果上方或左侧位置存在障碍物,则无法到达 [i,j][i,j] 位置,此时对应的路径数应设为0。因此我们可以得出结论:

    • 如果位置 [i,j] 有障碍物,那么 dp[i][j]=0;否则,状态转移方程为:
    dp[i][j]=dp[i−1][j]+dp[i][j−1]
  3. 初始化
    为了便于后续状态表的填充,建议在表的前面添加一行和一列以作为辅助节点。以下是需注意的要点:

    • 辅助节点值的设置:确保辅助节点值的设置能够保证后续计算的正确性。
    • 下标映射:添加的行列后,需特别关注下标的正确映射。

    在本题中,添加一行和一列后,将 dp[1][0] 初始化为 1,表示从起始位置出发的路径数。

  4. 填表顺序
    根据状态转移公式的推导,填表应按照以下顺序进行:

    • 从上到下逐行填充;
    • 在填充每一行时,从左到右逐列填充。
  5. 返回值
    最后,返回 dp[m][n] 的值,以获取结束位置的路径数量。

C++:

class Solution {
public:
 int uniquePathsWithObstacles(vector<vector<int>>& ob) {
 // 1. 创建 dp 表 
 // 2. 初始化 
 // 3. 填表 
 // 4. 返回值 
 int m = ob.size(), n = ob[0].size();
 vector<vector<int>> dp(m + 1, vector<int>(n + 1));
 dp[1][0] = 1;
 for(int i = 1; i <= m; i++)
 for(int j = 1; j <= n; j++)
 if(ob[i - 1][j - 1] == 0)
 dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
 return dp[m][n];
 }
};

Java:

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

算法思路(166)

  1. 状态表示

     

    在处理这类“路径类”问题时,状态表示通常有两种形式:

    • 形式一:从位置 [i,j] 出发的路径数。
    • 形式二:从起始位置出发到达位置 [i,j] 的路径数。

    本题中,我们采用第二种形式进行状态定义,即:

    dp[i][j] 表示到达位置 [i,j] 时所能获得的最大价值。
  2. 状态转移方程

     

    当我们考虑 dp[i][j] 的值时,可以得出以下两种到达 [i,j] 位置的方式:

    • 从上方:通过位置 [i−1,j] 向下移动,能获得的礼物价值为:dp[i−1][j]+grid[i][j]
    • 从左侧:通过位置 [i,j−1] 向右移动,能获得的礼物价值为:dp[i][j−1]+grid[i][j]

    因此,为了获得最大价值,状态转移方程可以表示为:

    dp[i][j]=max⁡(dp[i−1][j],dp[i][j−1])+grid[i][j]
  3. 初始化

     

    为了初始化状态表,可以在状态表的前方添加一行及一列作为辅助节点。在进行此操作时,应注意以下两个关键点:

    • 辅助节点值的设置:确保辅助节点内的值能够保证后续填表时的正确性。
    • 下标映射关系:添加行列后,务必关注下标映射的变化。

    在本题中,添加一行和一列后,可以将所有值初始化为 0。

  4. 填表顺序

     

    根据状态转移方程,填表的顺序为:

    • 逐行从上到下进行填充;
    • 逐列从左到右进行填充。
  5. 返回值

     

    根据状态表的定义,最终返回的结果为:

    dp[m][n]

    该值表示从起始位置到达位置 [m,n] 时能够获得的最大价值。

C++:

class Solution 
{
public:
 int maxValue(vector<vector<int>>& grid) 
 {
 // 1. 创建 dp 表 
 // 2. 初始化 
 // 3. 填表 
 // 4. 返回结果 
 int m = grid.size(), n = grid[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 - 1][j], dp[i][j - 1]) + grid[i - 1][j - 
1];
 return dp[m][n];
 }
};

Java:

class Solution 
{
 public int maxValue(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 = 1; i <= m; i++)
 for(int j = 1; j <= n; j++)
 dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]) + grid[i - 1]
[j - 1];
 return dp[m][n];
 }
}

算法思路(931)

对于这类路径问题,由于我们先前已处理过类似题型,因此“状态表示”和“状态转移”相对容易分析。相对而言,比较复杂的地方在于“边界条件”的处理。以下是详细的算法思路:

  1. 状态表示

     

    在解决此类“路径类”问题时,通常采用两种状态表示形式:

    • 形式一:从位置 [i,j] 出发,到达目标位置的路径数量。
    • 形式二:从起始位置出发,到达位置 [i,j] 的路径数量。

    本题选择第二种形式进行状态定义:

    dp[i][j] 表示到达位置 [i,j] 时,所有下降路径中的最小和。
  2. 状态转移方程

     

    对于一般位置 [i,j],根据题意,达到该位置可能有三种情况:

    • 从正上方位置 [i−1,j] 转移到 [i,j];
    • 从左上方位置 [i−1,j−1] 转移到 [i,j];
    • 从右上方位置 [i−1,j+1] 转移到 [i,j]。

    为了找到到达 [i,j][i,j] 的最小值,我们需要对这三种情况的最小值进行比较,然后加上矩阵中 [i,j][i,j] 位置的值。因此,状态转移方程为:

    dp[i][j]=min⁡(dp[i−1][j],min⁡(dp[i−1][j−1],dp[i−1][j+1]))+matrix[i][j]
  3. 初始化

     

    在状态表的前面添加辅助节点以便初始化。在进行此操作时,需要注意以下两个要点:

    • 辅助节点值的设置:确保辅助节点中的值能保证后续填表时的正确性。
    • 下标映射关系:在本题中,需要添加一行和两列作为辅助节点。所有新位置的值初始设为无穷大(∞),而第一行的值则初始化为0。
  4. 填表顺序

     

    根据状态表示,我们需按顺序以“从上到下”的方式填充状态表。

  5. 返回值

     

    请注意,题目并不要求返回 dp[m][n] 的值。题意是要求到达最后一行即可,因此我们需要返回“状态表中最后一行的最小值”,即:

    min⁡(dp[最后一行][j])

C++:

class Solution 
{
public:
 int minFallingPathSum(vector<vector<int>>& matrix) 
 {
 // 1. 创建 dp 表 
 // 2. 初始化 
 // 3. 填表 
 // 4. 返回结果 
 int n = matrix.size();
 vector<vector<int>> dp(n + 1, vector<int>(n + 2, INT_MAX));
 // 初始化第⼀⾏ 
 for(int j = 0; j < n + 2; j++) dp[0][j] = 0;
 for(int i = 1; i <= n; 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[n][j]);
 
 return ret;
 }
};

Java:

class Solution 
{
 public int minFallingPathSum(int[][] matrix) 
 {
 // 1. 创建 dp 表 
 // 2. 初始化 
 // 3. 填表 
 // 4. 返回结果 
 int n = matrix.length;
 int[][] dp = new int[n + 1][n + 2];
 for(int i = 1; i <= n; i++) dp[i][0] = dp[i][n + 1] = 
Integer.MAX_VALUE;
 for(int i = 1; i <= n; i++)
 for(int j = 1; j <= n; j++)
 dp[i][j] = Math.min(dp[i - 1][j], Math.min(dp[i - 1][j - 1], 
dp[i - 1][j + 1])) + matrix[i - 1][j - 1];
 
 int ret = Integer.MAX_VALUE;
 for(int j = 1; j <= n; j++)
 ret = Math.min(ret, dp[n][j]);
 return ret;
 }

相关文章:

  • Laravel Trait 实现 统一JSON 响应格式
  • CSS定位
  • 观察者模式在Java微服务间的使用
  • 学习大模型需要具备哪些技术、知识和基础
  • 【蓝桥杯速成】| 17.完全背包(一维easy版)
  • 题解:P8667 [蓝桥杯 2018 省 B] 递增三元组 (暴力+二分)
  • DeepSeek原生稀疏注意力(Native Sparse Attention, NSA)算法介绍
  • 【SpringCloud】LoadBalance-负载均衡
  • html处理Base文件流
  • 【C++项目】从零实现RPC框架「三」:项⽬抽象层实现
  • 动手实现docker全过程
  • python求解非线性方程组
  • 蓝桥杯——统计子矩阵
  • 设计模式学习(1)
  • 顺据结构(C\C++)——双向链表
  • 【Qt】游戏场景和图元
  • rbpf虚拟机-JIT和解释执行对比
  • 数据处理的两种范式:深入解析OLTP与OLAP系统
  • 自动驾驶实验
  • 13届省赛python A组:10.数的拆分
  • 网站的优化方案怎么写/代理广告投放平台
  • pc28网站开发/怎么制作网站?
  • 新手怎么做自己网站广告/关键词有哪些?
  • 山东网站seo公司/网站百度百科
  • 公众号如何推广产品/seo工作
  • 做长图网站/搜索引擎优化指的是什么