NOIP 2000 提高组 洛谷1004题(方格取数)解题思路与C++代码解析 动态规划优化路径选择

引言
洛谷1004题“方格取数”是算法竞赛中经典的动态规划问题,要求从网格中选取两条不交叉的路径,使路径上的数字和最大化。本文将结合您提供的代码,详细解析解题思路、动态规划状态设计、代码实现细节,并融入SEO优化技巧,帮助读者深入理解算法逻辑,同时提升文章搜索引擎友好度。
一、题目描述
简要描述题目:例如,在一个n×n的方格图中,每个格子包含一个正整数。需要选择两条从左上角到右下角的路径,路径可重复经过格子,但两条路径除起点和终点外不能相交。求两条路径数字和的最大值。
二、解题思路与算法分析
1. 问题分析
2. 动态规划设计
(1)状态定义使用四维数组f[k][i][j]表示:
k:两条路径共同走过的步数(即第k步);i:第一条路径当前所在的行坐标; j:第二条路径当前所在的列坐标。状态值f[k][i][j]为两条路径分别走到(i,j)和某个位置时的最大数字和。
(2)边界条件
f[0][1][1] = g[1][1](两条路径均从起点(1,1)出发,数字和为起点格子值)。 其他状态初始化为负无穷(-0x3f),避免非法状态影响结果。
(3)状态转移方程分情况讨论:
i == j时,两条路径在同一位置(重复点),只能同时向下或向右移动: (注:g[i][k+2-i]表示当前重复点的数字,因为两条路径同时移动一步。)i!= j时,两条路径独立移动,可分别向下或向右: (注:g[i][k+2-i]和g[j][k+2-j]分别表示两条路径当前位置的数字。)(4)最终答案:f[2*n-2][n][n](两条路径均到达右下角时的最大和)。
边界处理:通过min(n, k+1)限制循环范围,避免无效计算。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 15; // 网格最大维度
int g[N][N], f[N*2][N][N]; // g存储原网格,f为动态规划数组
int main() {
int n, x, y, v; // n为网格大小,x,y,v为输入的坐标和值
cin >> n; // 读取网格大小
// 输入网格数据(注意:用户代码可能未处理EOF情况,但洛谷题目通常无影响)
while((cin >> x >> y >> v) && (x || y || v)) {
g[x][y] = v; // 将数据存入g数组
}
// 初始化动态规划数组(除起点外,其余状态设为负无穷)
memset(f, -0x3f, sizeof f);
f[0][1][1] = g[1][1]; // 起点状态:两条路径均从(1,1)出发,和为g[1][1]
// 动态规划主循环
for(int k = 1; k <= 2*n-2; ++k) { // k为总步数(两条路径共走2n-2步)
for(int i = 1; i <= min(n, k+1); ++i) { // 第一条路径的行坐标
for(int j = 1; j <= min(n, k+1); ++j) { // 第二条路径的列坐标
int &val = f[k][i][j]; // 引用简化代码
val = max({
f[k-1][i][j], // 路径1不动,路径2移动
f[k-1][i-1][j], // 路径1向下,路径2不动
f[k-1][i][j-1], // 路径1不动,路径2向右
f[k-1][i-1][j-1] // 路径1向下,路径2向右
});
if(i == j) val += g[i][k+2-i]; // 重复点:两条路径同时移动,加一次数字
else val += g[i][k+2-i] + g[j][k+2-j]; // 非重复点:两条路径分别移动,加两次数字
}
}
}
// 输出结果
cout << f[2*n-2][n][n] << endl; // 两条路径均到达右下角时的最大和
return 0;
}f[k][i][j]的循环顺序:先遍历步数k,再遍历坐标i和j,确保状态依赖正确。min(n, k+1)限制循环范围,避免超出网格边界。 使用max({})语法简化状态转移表达式,提升代码可读性。
扩展方向:若网格规模更大,可尝试更激进的空间优化(如滚动数组+对称性压缩),或结合剪枝策略减少无效状态计算。
六、总结
洛谷1004题通过动态规划的四维状态设计,巧妙解决了两条不交叉路径的最优选择问题。用户提供的代码通过滚动数组优化空间,利用简洁的状态转移方程高效求解。掌握此类问题的关键在于理解状态定义与路径不交叉的约束条件,结合合理的边界处理,可实现高效算法。
原创内容 转载请注明出处
