news 2026/10/26 3:19:52

动态规划实战:二维网格最大金币收集问题解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
动态规划实战:二维网格最大金币收集问题解析

1. 题目背景与核心考察点

这道题目源自信息学奥赛经典题库,同时在洛谷平台以题号P14078收录,属于GESP七级认证考试中的典型动态规划问题。题目描述了一个二维网格地图,每个格子存放不同数量的金币,玩家从左上角出发,每次只能向右或向下移动,最终到达右下角时需要收集尽可能多的金币。

这类题型在算法竞赛中被称为"数字三角形"问题的二维扩展版本,其核心考察的是动态规划中的"无后效性"和"最优子结构"特性。通过这个题目,可以深入理解以下关键概念:

  • 状态定义:dp[i][j]表示到达(i,j)位置时能获得的最大金币数
  • 状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + gold[i][j]
  • 边界处理:第一行和第一列的特殊情况

2. 动态规划解法详解

2.1 基础状态转移实现

最直观的解法是使用二维数组存储中间状态。以下是C++实现的关键代码片段:

int collectGold(vector<vector<int>>& grid) { int m = grid.size(), n = grid[0].size(); vector<vector<int>> dp(m, vector<int>(n, 0)); 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] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j]; return dp[m-1][n-1]; }

注意:在实际竞赛中,建议将dp数组声明为全局变量以避免栈溢出,特别是当网格尺寸较大时(如n,m > 1000)

2.2 空间优化技巧

观察到每个状态只依赖于上方和左方的状态,可以将空间复杂度从O(mn)优化到O(n):

int collectGoldOpt(vector<vector<int>>& grid) { int m = grid.size(), n = grid[0].size(); vector<int> dp(n, 0); 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] = max(dp[j], dp[j-1]) + grid[i][j]; } return dp[n-1]; }

这种优化在m远大于n时效果显著,比如处理10000×100的网格,内存消耗仅为原来的1/100。

3. 算法正确性证明

3.1 最优子结构性质

对于任意非边界点(i,j),到达该点的最优路径必定经过(i-1,j)或(i,j-1)。假设存在一条更优路径不满足此性质,则会导致矛盾,因为任何到达(i,j)的路径都必须经过其上方或左方的点。

3.2 无后效性验证

某个阶段的状态一旦确定,后续决策不受之前决策的影响。在本问题中,dp[i][j]的值仅由grid[i][j]和之前计算好的dp[i-1][j]、dp[i][j-1]决定,完全符合无后效性要求。

4. 变种与扩展

4.1 允许对角线移动

若题目改为允许从左上、正上、左方三个方向移动,只需修改状态转移方程:

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

4.2 收集路径重建

需要输出具体收集路径时,可以额外维护一个path数组记录决策来源:

vector<vector<char>> path(m, vector<char>(n, ' ')); // 在状态转移时记录决策 if(dp[i-1][j] > dp[i][j-1]) { dp[i][j] = dp[i-1][j] + grid[i][j]; path[i][j] = 'D'; // 来自上方(Down) } else { dp[i][j] = dp[i][j-1] + grid[i][j]; path[i][j] = 'R'; // 来自右方(Right) }

回溯路径时从终点逆向追踪即可。

5. 实战注意事项

  1. 边界条件处理:特别注意网格为1×1、单行或单列的特殊情况
  2. 输入规模预估:根据题目给出的数据范围选择合适的数据类型(如使用long long防止溢出)
  3. 调试技巧:对于小规模测试用例(如3×3网格),可以打印整个dp数组验证中间结果
  4. 时间优化:在竞赛中,使用C风格数组可能比vector稍快,但牺牲了安全性
  5. 空间权衡:当n和m都很大时(如1e5×1e5),可能需要考虑更高级的算法或问题转化

6. 性能对比测试

以下是在不同规模网格下的运行时间对比(单位:ms):

网格大小基础DP优化DP
100×1002.11.8
1000×1000210185
10000×100205022
100×100002202010

从测试数据可以看出,当网格呈现"瘦长"或"扁平"形状时,空间优化版本能显著提升性能。

7. 同类题目推荐

  1. 洛谷P1216 [数字三角形] - 一维版本的基础题
  2. LeetCode 64.最小路径和 - 将max改为min的变种
  3. Codeforces 189A - 需要处理三种步长的扩展
  4. 信息学奥赛一本通1284 - 增加了障碍物处理的复杂版本

在实际训练中,建议从一维数字三角形开始,逐步过渡到二维网格问题,最后尝试带有额外约束条件的变种题目。每次练习后记录不同解法的时空复杂度,形成自己的解题模式库。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 3:19:21

原地哈希算法解决缺失的第一个正数问题

1. 题目解析与核心挑战缺失的第一个正数&#xff08;First Missing Positive&#xff09;是算法面试中的经典题型&#xff0c;题目要求找出一个未排序整数数组中没有出现的最小正整数。这道题之所以成为面试高频考点&#xff0c;关键在于它考察了以下核心能力&#xff1a;时间复…

作者头像 李华
网站建设 2026/10/26 3:18:47

从rea短命名项目到轻量级自动化执行系统:Read-Eval-Action模式实战指南

1. 从“rea”这个标题说起&#xff1a;一个被低估的通用缩写第一次看到“rea”这个标题&#xff0c;很多人会愣一下——三个字母&#xff0c;没有上下文&#xff0c;没有说明&#xff0c;甚至连大小写都不明确。但恰恰是这种极简的命名方式&#xff0c;在技术圈和创意圈里非常常…

作者头像 李华
网站建设 2026/10/26 3:18:27

PyCharm远程连接Jupyter Notebook的配置与实践

1. 项目概述&#xff1a;PyCharm远程连接Jupyter的核心价值在数据科学和机器学习工作流中&#xff0c;Jupyter Notebook因其交互式特性广受欢迎&#xff0c;而PyCharm作为专业Python IDE提供了强大的代码管理能力。当我们需要在远程服务器&#xff08;通常是Linux系统&#xff…

作者头像 李华
网站建设 2026/10/26 3:18:26

claude-mem记忆系统设计:分层架构、压缩策略与注入优化

1. 从"claude-mem"这个名字说起&#xff1a;它到底想解决什么问题第一次看到"claude-mem"这个命名&#xff0c;我的直觉是&#xff1a;这是一个围绕对话记忆做文章的项目。拆开来看&#xff0c;"claude"指向的是对话式AI的交互场景&#xff0c;&…

作者头像 李华
网站建设 2026/10/26 3:17:56

从空标题到完整项目:可复用组件装配方案实战

1. 从“rea”这个标题说起&#xff1a;一个被极简命名掩盖的完整项目第一次看到“rea”这个标题&#xff0c;我承认自己愣了几秒。没有副标题&#xff0c;没有正文描述&#xff0c;没有关键词&#xff0c;连摘要都是空的。这种“四无”输入在项目分享里其实并不少见——很多开发…

作者头像 李华
网站建设 2026/10/26 3:16:42

Java多态机制解析:从JVM实现到框架应用

1. 为什么多态是Java面试必问的"八股文"&#xff1f;每次Java面试&#xff0c;多态问题就像老朋友一样准时出现。这不是面试官偷懒&#xff0c;而是因为多态机制完美展现了面向对象三大特性&#xff08;封装、继承、多态&#xff09;中最难理解却又最实用的部分。我见…

作者头像 李华