当前位置:首页 > 比赛题解 > NOIP 2000 提高组 洛谷1004题(方格取数)解题思路与C++代码解析 动态规划优化路径选择

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

3天前比赛题解50
截图未命名.jpg NOIP 2000 提高组 洛谷1004题(方格取数)解题思路与C++代码解析 动态规划优化路径选择 洛谷1004题 动态规划 四维dp 路径优化 C++代码 第1张

引言

洛谷1004题“方格取数”是算法竞赛中经典的动态规划问题,要求从网格中选取两条不交叉的路径,使路径上的数字和最大化。本文将结合您提供的代码,详细解析解题思路、动态规划状态设计、代码实现细节,并融入SEO优化技巧,帮助读者深入理解算法逻辑,同时提升文章搜索引擎友好度。


一、题目描述

    简要描述题目:例如,在一个n×n的方格中,每个格子包含一个正整数。需要选择两条从左上角到右下角的路径,路径可重复经过格子,但两条路径除起点和终点外不能相交。求两条路径数字和的最大值。


二、解题思路与算法分析

1. 问题分析

    1.问题核心是求解两条不交叉路径的最优组合,属于路径规划与组合优化问题。
    2.直接暴力枚举路径组合的时间复杂度极高(O(2^n×n)),需采用动态规划降低复杂度。
    3.关键点:如何定义状态以表示两条路径的当前位置,并确保状态转移时不交叉。

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](两条路径均到达右下角时的最大和)。

3. 优化技巧
    空间优化:由于状态转移仅依赖前一步的四个状态,可使用滚动数组进一步降低空间复杂度,但用户代码已通过三维数组实现(f[k][i][j]),有效避免了高维数组的存储问题。

    边界处理:通过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({})语法简化状态转移表达式,提升代码可读性。


四、算法优化与扩展思考
    时间复杂度:O(n^3),空间复杂度O(n^2),已满足题目要求。

    扩展方向:若网格规模更大,可尝试更激进的空间优化(如滚动数组+对称性压缩),或结合剪枝策略减少无效状态计算。


五、SEO关键词与优化建议
    关键词:洛谷1004题、动态规划、方格取数、四维dp路径优化C++代码实现
    优化点
标题含核心关键词,符合用户搜索意图。
正文使用H标签分级,关键步骤详细分解,提升可读性。
代码高亮并带注释,增强技术专业性。
加入算法优化与扩展部分,增加文章深度和权威性。


六、总结

    洛谷1004题通过动态规划的四维状态设计,巧妙解决了两条不交叉路径的最优选择问题。用户提供的代码通过滚动数组优化空间,利用简洁的状态转移方程高效求解。掌握此类问题的关键在于理解状态定义与路径不交叉的约束条件,结合合理的边界处理,可实现高效算法。



原创内容 转载请注明出处

分享给朋友:

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。