2020年CSP-J 方格取数问题详解:双向动态规划解法与路径优化策略
一、问题背景与需求分析
题目要求在一个n×m的方格矩阵中,从左上角(0,0)出发到右下角(n-1,m-1),每次可以向右、向上或向下移动,找出路径上数字之和最大的路线。
核心难点:
路径方向多样性(右、上、下)
避免路径交叉和循环
需要考虑来自不同方向的最优解
二、完整代码实现(带详细注释)
#include <iostream> #include <vector> #include <algorithm> using namespACe std; const int INF = 1e9; // 定义无穷大值 int main() { ios::sync_with_stdio(false); // 关闭同步提升IO速度 cin.tie(nullptr); // 解除cin与cout的绑定 int n, m; cin >> n >> m; // 读取网格数据 vector<vector<int>> grid(n, vector<int>(m)); for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { cin >> grid[i][j]; } } // 三维DP数组:dp[i][j][0]表示从上方到达(i,j)的最大和 // dp[i][j][1]表示从下方到达(i,j)的最大和 vector<vector<vector<long long>>> dp(n, vector<vector<long long>>(m, vector<long long>(2, -INF))); // 初始化起点 dp[0][0][0] = dp[0][0][1] = grid[0][0]; // 处理第一列(只能从上到下) for (int i = 1; i < n; ++i) { dp[i][0][0] = dp[i][0][1] = dp[i-1][0][0] + grid[i][0]; } // 动态规划处理(按列处理) for (int j = 1; j < m; ++j) { // 从上到下处理当前列 for (int i = 0; i < n; ++i) { if (i == 0) { // 第一行只能从左边来 dp[i][j][0] = max(dp[i][j-1][0], dp[i][j-1][1]) + grid[i][j]; } else { // 可以从左边或上方来 dp[i][j][0] = max({dp[i][j-1][0], dp[i][j-1][1], dp[i-1][j][0]}) + grid[i][j]; } } // 从下到上处理当前列 for (int i = n-1; i >= 0; --i) { if (i == n-1) { // 最后一行只能从左边来 dp[i][j][1] = max(dp[i][j-1][0], dp[i][j-1][1]) + grid[i][j]; } else { // 可以从左边或下方来 dp[i][j][1] = max({dp[i][j-1][0], dp[i][j-1][1], dp[i+1][j][1]}) + grid[i][j]; } } } // 输出结果(取两种方向中的最大值) cout << max(dp[n-1][m-1][0], dp[n-1][m-1][1]) << endl; return 0; }
三、算法核心思想解析
三维DP设计:
第三维[0]/[1]分别表示从上/从下到达该点
避免路径方向冲突
双向处理策略:
每列先从上到下计算(考虑来自左和上的路径)
再从下到上计算(考虑来自左和下的路径)
边界处理:
第一列只能垂直移动
第一行和最后一行有特殊处理
四、复杂度分析与优化
五、常见错误与调试技巧
初始化问题:忘记初始化起点
方向混淆:上下方向处理错误
边界条件:行列边界处理不当
调试建议:打印每步DP值验证
原创内容 转载请注明出处