题解:洛谷 P1004 方格取数

【题目来源】

洛谷:P1004 [NOIP 2000 提高组] 方格取数 - 洛谷

【题目描述】

设有 \(N\times N\) 的方格图(\(N\le 9\)),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字 \(0\)。如下图所示(见样例):

某人从图的左上角的 \(A\) 点出发,可以向下行走,也可以向右走,直到到达右下角的 \(B\) 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字 \(0\))。
此人从 \(A\) 点到 \(B\) 点共走两次,试找出 \(2\) 条这样的路径,使得取得的数之和为最大。

【输入】

输入的第一行为一个整数 \(N\)(表示 \(N\times N\) 的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的 \(0\) 表示输入结束。

只需输出一个整数,表示 \(2\) 条路径上取得的最大的和。

【输出】

只需输出一个整数,表示 \(2\) 条路径上取得的最大的和。

【输入样例】

8
2 3 13
2 6  6
3 5  7
4 4 14
5 2 21
5 6  4
6 3 15
7 2 14
0 0  0

【输出样例】

67

【核心思想】

  1. 问题分析:给定 \(N \times N\) 方格图,从左上角 \((1,1)\) 到右下角 \((N,N)\) 走两次(每次只能向下或向右),取走经过方格中的数(取走后变为 \(0\))。求两条路径取得的最大数字之和。这是一个四维线性 DP 问题,核心在于将两条路径同步处理,状态记录两条路径的当前位置。

  2. 算法选择

    • 四维 DP\(dp[i][j][k][l]\) 表示第一条路径走到 \((i,j)\)、第二条路径走到 \((k,l)\) 时的最大和
    • 同步推进:两条路径同时从 \((1,1)\) 出发,每步各向下或向右走一格,因此 \(i+j = k+l\)(步数相同)
  3. 关键步骤

    • 初始化:读取 \(N\) 和网格数据 \(a[x][y]\)\(dp\) 数组初始化为 \(0\)
    • 四维 DP 递推\(i, j, k, l\)\(1\)\(N\)):
      • 仅当 \(i + j = k + l\) 时处理(保证两条路径步数同步)
      • 状态转移:两条路径上一步各有 \(2\) 种选择(从上方或左方来),共 \(4\) 种组合:
        • 路径1从 \((i-1,j)\) 来,路径2从 \((k-1,l)\) 来:\(dp[i-1][j][k-1][l]\)
        • 路径1从 \((i-1,j)\) 来,路径2从 \((k,l-1)\) 来:\(dp[i-1][j][k][l-1]\)
        • 路径1从 \((i,j-1)\) 来,路径2从 \((k-1,l)\) 来:\(dp[i][j-1][k-1][l]\)
        • 路径1从 \((i,j-1)\) 来,路径2从 \((k,l-1)\) 来:\(dp[i][j-1][k][l-1]\)
      • \(dp[i][j][k][l] = \max(\text{四种组合}) + a[i][j] + a[k][l]\)
      • 去重:若 \((i,j) = (k,l)\)(两条路径重合),该格子的数只取一次,减去 \(a[k][l]\)
    • 输出答案\(dp[N][N][N][N]\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^4)\),四层循环,\(N \leq 9\) 时完全可接受
    • 空间复杂度:\(O(N^4)\),四维 DP 数组
  5. 线性 DP 的核心思想

    • 同步状态设计:将两条路径的行走过程同步化,用四维状态同时记录两条路径的位置,避免分别计算后处理重叠的复杂性
    • 步数一致性约束\(i+j = k+l\) 确保两条路径走了相同的步数,这是同步推进的关键
    • 四种转移全覆盖:每条路径各有 \(2\) 种来向,组合成 \(4\) 种转移,取最大值保证最优性
    • 重叠处理:两条路径经过同一格子时,该格子的值只应被计算一次,通过判断 \((i,j) = (k,l)\) 并减去重复值实现
    • 降维优化:可利用 \(i+j = k+l = s\) 将四维降为三维 \(dp[s][i][k]\),但四维写法更直观
    • 适用于多路径同步、网格行走、重叠去重类 DP 问题

【解题思路】

【算法标签】

普及+ #线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int a[15][15];          // 存储网格中的数值
int dp[15][15][15][15];  // 四维DP数组,记录两条路径的最大和
int n, x, y, num;        // n: 网格大小,x/y: 坐标,num: 数值

/**
 * 辅助函数:返回四个数中的最大值
 */
int f(int a, int b, int c, int d)
{
    return max(max(a, b), max(c, d));
}

int main()
{
    // 输入网格大小n
    cin >> n;

    // 输入网格数据,直到遇到x=0结束
    while (cin >> x >> y >> num && x)
    {
        a[x][y] = num;
    }

    // 动态规划计算两条路径的最大和
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            for (int k = 1; k <= n; k++)
            {
                for (int l = 1; l <= n; l++)
                {
                    // 确保两条路径同步前进(i+j == k+l)
                    if (i + j == k + l)
                    {
                        // 状态转移:取四种可能的最大值加上当前两个点的值
                        dp[i][j][k][l] = f(dp[i-1][j][k-1][l],
                                          dp[i-1][j][k][l-1],
                                          dp[i][j-1][k-1][l],
                                          dp[i][j-1][k][l-1])
                                          + a[i][j] + a[k][l];

                        // 如果两条路径重合,减去重复计算的值
                        if (i == k && j == l)
                        {
                            dp[i][j][k][l] -= a[k][l];
                        }
                    }
                }
            }
        }
    }

    // 输出最终结果(原代码缺少输出语句,这里保持原样)
    return 0;
}

【运行结果】

8
2 3 13
2 6  6
3 5  7
4 4 14
5 2 21
5 6  4
6 3 15
7 2 14
0 0  0
67
posted @ 2026-08-13 14:14  团爸讲算法  阅读(29)  评论(0)    收藏  举报