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

网格路径问题(Grid Path Problem)是动态规划的经典应用之一,广泛用于解决在网格中寻找路径数量、最短路径或带约束的路径问题

网格路径问题(Grid Path Problem)是动态规划的经典应用之一,广泛用于解决在网格中寻找路径数量、最短路径或带约束的路径问题。

它的核心思想是将问题分解为子问题,通过状态转移计算从起点到终点的路径信息,适合初学者理解动态规划的状态定义、转移方程和边界处理。以下以经典的网格路径问题为例,详细讲解其动态规划解法,并提供 C# 实现,结合斐波那契数列和回文串问题的思想,突出动态规划的通用性。


一、网格路径问题的核心思想

1. 问题描述网格路径问题通常定义在一个 m × n 的网格中,目标是从左上角 (0,0) 移动到右下角 (m-1,n-1),每次只能向右或向下移动。常见变种包括:

  • 路径数量:计算所有可能的路径数。
  • 最短路径:在带权网格中寻找最小路径和。
  • 带障碍:网格中某些点不可达,计算可行路径数或最短路径。

2. 动态规划的核心思想

  • 重叠子问题:网格中的每个点 (i,j) 都可以从 (i-1,j)(上)或 (i,j-1)(左)到达,子问题重复出现。
  • 最优子结构:目标点 (m-1,n-1) 的解可以通过子问题 (i-1,j) 和 (i,j-1) 构建。
  • 状态定义:定义 dp[i][j] 表示从 (0,0) 到 (i,j) 的路径数量或路径和。
  • 状态转移:
    • 路径数量: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](带权网格)。
    • 带障碍:检查 (i,j) 是否可达,若不可达则跳过。
  • 边界条件:
    • 初始化第一行和第一列(通常只有一条路径或累加权重)。
    • 对于带障碍问题,障碍点的状态设为 0(路径数)或无穷大(路径和)。

3. 与斐波那契和回文串的联系

  • 斐波那契数列:一维 DP,状态依赖前两个值,网格路径是二维 DP,状态依赖上和左两个方向。
  • 回文串问题:二维 DP(如 dp[i][j] 判断回文),网格路径同样使用二维数组,但状态转移更简单(固定方向)。
  • 通用性:动态规划的关键在于定义状态和转移方程,网格路径问题展示了如何处理多维依赖和边界条件。

二、典型网格路径问题的 C# 实现以下以三种经典网格路径问题为例,提供 C# 实现,并逐步增加复杂性。

1. 网格路径数量(Unique Paths)问题:在一个 m × n 网格中,从 (0,0) 到 (m-1,n-1),每次只能向右或向下移动,计算总路径数。C# 实现:csharp

public class UniquePaths { public int CountPaths(int m, int n) { int[,] dp = new int[m, n]; // 初始化第一行和第一列 for (int i = 0; i < m; i++) dp[i, 0] = 1; for (int j = 0; j < n; j++) dp[0, j] = 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 - 1, n - 1]; } }

分析:

  • 状态定义:dp[i][j] 表示从 (0,0) 到 (i,j) 的路径数。
  • 状态转移:dp[i][j] = dp[i-1][j] + dp[i][j-1]。
  • 边界条件:第一行和第一列只有一条路径,dp[i][0] = 1,dp[0][j] = 1。
  • 时间复杂度:O(mn),空间复杂度:O(mn)。

空间优化:

  • 由于 dp[i][j] 只依赖上一行和当前行的值,可以使用滚动数组:

csharp

public int CountPathsOptimized(int m, int n) { int[] dp = new int[n]; Array.Fill(dp, 1); // 初始化第一行 for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { dp[j] += dp[j - 1]; } } return dp[n - 1]; }
  • 空间复杂度:O(n),使用一维数组存储当前行。

2. 最小路径和(Minimum Path Sum)问题:在一个 m × n 的网格中,每个格子有非负权重,求从 (0,0) 到 (m-1,n-1) 的最小路径和,只能向右或向下移动。C# 实现:csharp

public class MinimumPathSum { public int MinPathSum(int[][] grid) { int m = grid.Length, n = grid[0].Length; int[,] dp = new int[m, n]; // 初始化起点 dp[0, 0] = grid[0][0]; // 初始化第一行和第一列 for (int i = 1; i < m; i++) dp[i, 0] = dp[i - 1, 0] + grid[i][0]; for (int j = 1; j < n; j++) dp[0, j] = dp[0, j - 1] + grid[0][j]; // 动态规划 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][j]; } } return dp[m - 1, n - 1]; } }

分析:

  • 状态定义:dp[i][j] 表示从 (0,0) 到 (i,j) 的最小路径和。
  • 状态转移:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]。
  • 边界条件:第一行和第一列累加权重。
  • 时间复杂度:O(mn),空间复杂度:O(mn)。

空间优化:

  • 使用一维数组:

csharp

public int MinPathSumOptimized(int[][] grid) { int m = grid.Length, n = grid[0].Length; int[] dp = new int[n]; // 初始化第一行 dp[0] = grid[0][0]; for (int j = 1; j < n; j++) dp[j] = dp[j - 1] + grid[0][j]; // 动态规划 for (int i = 1; i < m; i++) { dp[0] += grid[i][0]; for (int j = 1; j < n; j++) { dp[j] = Math.Min(dp[j], dp[j - 1]) + grid[i][j]; } } return dp[n - 1]; }
  • 空间复杂度:O(n)。

3. 带障碍的网格路径(Unique Paths with Obstacles)问题:在一个 m × n 网格中,某些格子有障碍(标记为 1),不可通过,求从 (0,0) 到 (m-1,n-1) 的路径数。C# 实现:csharp

public class UniquePathsWithObstacles { public int UniquePathsWithObstacles(int[][] obstacleGrid) { int m = obstacleGrid.Length, n = obstacleGrid[0].Length; int[,] dp = new int[m, n]; // 初始化起点 dp[0, 0] = obstacleGrid[0][0] == 0 ? 1 : 0; // 初始化第一行和第一列 for (int i = 1; i < m; i++) { dp[i, 0] = (obstacleGrid[i][0] == 0 && dp[i - 1, 0] != 0) ? 1 : 0; } for (int j = 1; j < n; j++) { dp[0, j] = (obstacleGrid[0][j] == 0 && dp[0, j - 1] != 0) ? 1 : 0; } // 动态规划 for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { if (obstacleGrid[i][j] == 0) { dp[i, j] = dp[i - 1, j] + dp[i, j - 1]; } } } return dp[m - 1, n - 1]; } }

分析:

  • 状态定义:dp[i][j] 表示从 (0,0) 到 (i,j) 的路径数。
  • 状态转移:若 (i,j) 无障碍,dp[i][j] = dp[i-1][j] + dp[i][j-1];否则,dp[i][j] = 0。
  • 边界条件:起点或路径上有障碍时,路径数为 0。
  • 时间复杂度:O(mn),空间复杂度:O(mn).

空间优化:csharp

public int UniquePathsWithObstaclesOptimized(int[][] obstacleGrid) { int m = obstacleGrid.Length, n = obstacleGrid[0].Length; int[] dp = new int[n]; // 初始化第一行 dp[0] = obstacleGrid[0][0] == 0 ? 1 : 0; for (int j = 1; j < n; j++) { dp[j] = (obstacleGrid[0][j] == 0 && dp[j - 1] != 0) ? 1 : 0; } // 动态规划 for (int i = 1; i < m; i++) { dp[0] = (obstacleGrid[i][0] == 0 && dp[0] != 0) ? 1 : 0; for (int j = 1; j < n; j++) { if (obstacleGrid[i][j] == 0) { dp[j] += dp[j - 1]; } else { dp[j] = 0; } } } return dp[n - 1]; }
  • 空间复杂度:O(n)。

三、测试代码以下是测试所有实现的 C# 程序:csharp

using System; class Program { static void Main() { // 测试网格路径数量 var uniquePaths = new UniquePaths(); Console.WriteLine($"Unique Paths (3x7): {uniquePaths.CountPaths(3, 7)}"); // 28 Console.WriteLine($"Unique Paths Optimized (3x7): {uniquePaths.CountPathsOptimized(3, 7)}"); // 28 // 测试最小路径和 var minPathSum = new MinimumPathSum(); int[][] grid = new int[][] { new int[] {1, 3, 1}, new int[] {1, 5, 1}, new int[] {4, 2, 1} }; Console.WriteLine($"Minimum Path Sum: {minPathSum.MinPathSum(grid)}"); // 7 Console.WriteLine($"Minimum Path Sum Optimized: {minPathSum.MinPathSumOptimized(grid)}"); // 7 // 测试带障碍的网格路径 var uniquePathsWithObstacles = new UniquePathsWithObstacles(); int[][] obstacleGrid = new int[][] { new int[] {0, 0, 0}, new int[] {0, 1, 0}, new int[] {0, 0, 0} }; Console.WriteLine($"Unique Paths with Obstacles: {uniquePathsWithObstacles.UniquePathsWithObstacles(obstacleGrid)}"); // 2 Console.WriteLine($"Unique Paths with Obstacles Optimized: {uniquePathsWithObstacles.UniquePathsWithObstaclesOptimized(obstacleGrid)}"); // 2 } }

输出:

Unique Paths (3x7): 28 Unique Paths Optimized (3x7): 28 Minimum Path Sum: 7 Minimum Path Sum Optimized: 7 Unique Paths with Obstacles: 2 Unique Paths with Obstacles Optimized: 2

四、与斐波那契和回文串问题的对比

  1. 状态定义:
    • 斐波那契:一维 DP,dp[i] 依赖前两个状态。
    • 回文串:二维 DP,dp[i][j] 依赖内部子串(如 dp[i+1][j-1])。
    • 网格路径:二维 DP,dp[i][j] 依赖上和左两个方向,结构更直观。
  2. 状态转移:
    • 斐波那契:dp[i] = dp[i-1] + dp[i-2],固定依赖。
    • 回文串:依赖首尾字符和子问题,逻辑较复杂。
    • 网格路径:dp[i][j] = dp[i-1][j] + dp[i][j-1](路径数)或取最小值(路径和),方向固定。
  3. 优化空间:
    • 斐波那契优化到 O(1),网格路径优化到 O(min(m,n)),回文串优化到 O(n)。
    • 网格路径的空间优化(滚动数组)与斐波那契类似,依赖有限的先前状态。
  4. 问题扩展:
    • 网格路径问题可以扩展到带权路径、k 步路径、3D 网格等,类似回文串问题扩展到模糊匹配或多字符串匹配。

五、优化与扩展

  1. 空间优化:
    • 所有网格路径问题均可通过滚动数组将空间复杂度从 O(m*n) 降到 O(min(m,n))。
    • 对于特殊场景(如 m >> n),可以交换行列进一步优化。
  2. 数学解法:
    • 对于无障碍的路径数量问题,可用组合数公式 C(m+n-2, m-1) 解决,时间复杂度 O(min(m,n)):csharp
      public long CountPathsMath(int m, int n) { long result = 1; for (int i = 0; i < m - 1; i++) { result = result * (m + n - 2 - i) / (i + 1); } return result; }
  3. 变种问题:
    • k 步路径:限制移动步数,需增加状态维度,如 dp[i][j][k]。
    • 路径还原:记录转移方向,输出具体路径。
    • 多方向移动:允许对角线移动,修改状态转移方程。
  4. 实际应用:
    • 机器人导航:规划机器人在网格中的路径。
    • 游戏设计:计算角色移动的可能性或最优路径。
    • 网络优化:在网格化网络中寻找最小延迟路径。

六、总结网格路径问题是动态规划的经典案例,展示了如何通过二维状态数组解决路径相关问题。C# 实现中,路径数量、最小路径和和带障碍路径问题分别对应不同约束,均可通过滚动数组优化空间。相比斐波那契数列(一维 DP)和回文串问题(复杂二维 DP),网格路径问题状态转移更直观,适合入门学习。开发者可以从这些问题入手,逐步扩展到更复杂的动态规划问题,如字符串匹配、背包问题等。

http://www.jsqmd.com/news/865720/

相关文章:

  • Android-examples 项目路线图:未来发展方向与社区贡献指南
  • 5个必学的 libev 实战技巧:从基础定时器到复杂异步编程
  • 2026年第二季度重庆化粪池清掏服务专业机构盘点与联系指南 - 2026年企业推荐榜
  • ThinkPHP-BJYAdmin即时通讯集成:融云聊天室与消息推送实现
  • 3分钟掌握HTML转Figma:打通设计与开发的终极桥梁
  • 快速排序(Quick Sort)是一种高效的排序算法,基于分治思想,通过选择一个“基准”(pivot)将数组划分为两个子数组,递归排序。相比冒泡排序,快速排序在平均情况下性能更优,尤其适合大规模数据
  • 浏览器资源嗅探终极指南:猫抓Cat-Catch完整使用教程
  • Palette核心架构深度剖析:UNet、扩散模型与注意力机制详解
  • 为什么顶尖科技公司禁用ChatGPT默认设置?逆向解析FAANG内部《AI编程红线白皮书》核心条款
  • 济南焊接变位机厂家哪家好?靠谱变位机滚轮架设备厂家汇总 - 深度智识库
  • 2026 Linux 视频播放器排行|13 款全能 / 轻量 / 高清播放神器
  • 2026年适合国央企的OpenClaw国产化替代平台,支持本地化部署工具推荐 - 品牌2025
  • 独立开发者如何借助taotoken为个人项目选择性价比最高的ai模型
  • Shairport4w:Windows电脑的终极AirPlay音频接收器完整指南
  • 抖音视频批量下载终极指南:3分钟快速上手无水印下载工具
  • 昇腾CANN向量索引生成API
  • 5分钟完成专业摄影作品水印:semi-utils批量EXIF参数自动化工具终极指南
  • Jooby Session管理:从内存存储到Redis集群的演进之路
  • 免费解锁AMD Ryzen隐藏性能:SMUDebugTool完全指南
  • 2026 拉萨特产采购指南:罗布麦赞成火车站片区首选 仓储式模式重塑行业标准 - 资讯速览
  • 冠珠瓷砖揽获新锐榜“陶瓷领军品牌”、“年度产品金奖”、“品质金奖”
  • wxauto微信自动化终极指南:释放双手,让微信工作更高效
  • libev 多平台适配指南:在 Linux、Windows 和 macOS 上部署事件驱动应用
  • 从文本到电影级运镜:Sora 2提示词编排术(含动态景深/运动矢量/光照衰减参数表)
  • 【技术架构深度解析】Baiduwp-PHP:基于API逆向工程的百度网盘链接解析方案
  • 合同管理太头疼?从起草到归档,每一步都帮你理清楚
  • TexasSolver:高效德州扑克GTO求解器的深度技术解析与实战指南
  • CANN/asc-devkit SIMD矢量除法API
  • CANN/pypto 减法操作函数
  • 口腔执业医师考试哪个老师讲题思路清晰?深度测评来了! - 医考机构品牌测评专家