题目描述

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

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

输入格式

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

输出格式

只需输出一个整数,表示 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≤9。

题目解析与解法介绍

我们面对的是一个经典的动态规划问题,题目要求在给定的 N×N 方格图中找到两条从左上角 A 点到右下角 B 点的路径,使得路径上取得的数之和最大。每个方格中的数字只能取一次,并且每次移动只能向右或向下。

输入输出格式
  • 输入:首先是一个整数 N(表示 N×N 的方格图),接下来的每行有三个整数,前两个表示位置(x, y),第三个数为该位置上所放的数。一行单独的 0 0 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

我们需要构造出一个 8×8 的矩阵,并将给定的数值填入对应的位置。其余未提及的位置默认值为 0。

解题思路

为了求解这个问题,我们可以使用动态规划的方法。由于需要同时考虑两条路径,因此状态空间会稍微复杂一些。具体步骤如下:

  1. 初始化矩阵:根据输入数据初始化一个 N×N 的矩阵,未指定的位置设为 0。
  2. 定义状态:设 dp[i][j][k][l] 表示第一条路径走到 (i,j),第二条路径走到 (k,l) 时的最大和。
  3. 状态转移方程
    • 如果两条路径不重合,则当前最大和为 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] 中的最大值加上当前位置的值。
    • 如果两条路径重合,则当前最大和为 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] 中的最大值加上当前位置的值(只加一次)。
  4. 边界条件:初始状态为 dp[0][0][0][0] = grid[0][0]
  5. 最终结果dp[N-1][N-1][N-1][N-1] 即为所求的最大和。

代码实现

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

const int MAX_N = 9;
int grid[MAX_N][MAX_N];
int dp[MAX_N+1][MAX_N+1][MAX_N+1][MAX_N+1];

void initializeGrid(int N) {
    memset(grid, 0, sizeof(grid));
    memset(dp, 0, sizeof(dp));

    int x, y, value;
    while (true) {
        cin >> x >> y >> value;
        if (x == 0 && y == 0 && value == 0) break;
        grid[x-1][y-1] = value;  // 转换为0-based索引
    }
}

int maxPathSum(int N) {
    dp[0][0][0][0] = grid[0][0];

    for (int i = 0; i <= N; ++i) {
        for (int j = 0; j <= N; ++j) {
            for (int k = 0; k <= N; ++k) {
                for (int l = 0; l <= N; ++l) {
                    if (i == 0 && j == 0 && k == 0 && l == 0) continue;

                    int prev_max = 0;
                    if (i > 0 && k > 0) prev_max = max(prev_max, dp[i-1][j][k-1][l]);
                    if (i > 0 && l > 0) prev_max = max(prev_max, dp[i-1][j][k][l-1]);
                    if (j > 0 && k > 0) prev_max = max(prev_max, dp[i][j-1][k-1][l]);
                    if (j > 0 && l > 0) prev_max = max(prev_max, dp[i][j-1][k][l-1]);

                    if (i != k || j != l) {  // 不重合
                        dp[i][j][k][l] = prev_max + grid[i-1][j-1] + grid[k-1][l-1];
                    } else {  // 重合
                        dp[i][j][k][l] = prev_max + grid[i-1][j-1];
                    }
                }
            }
        }
    }

    return dp[N][N][N][N];
}

int main() {
    int N;
    cin >> N;
    initializeGrid(N);
    cout << maxPathSum(N) << endl;
    return 0;
}

解题过程

  1. 初始化矩阵

    • 使用 memset 初始化 grid 和 dp 数组。
    • 读取输入数据并填充 grid,注意将输入的坐标转换为 0-based 索引。
  2. 动态规划核心逻辑

    • 定义 dp[i][j][k][l] 为两条路径分别到达 (i,j) 和 (k,l) 时的最大和。
    • 通过四个循环遍历所有可能的状态组合。
    • 对于每个状态,计算其前驱状态的最大值,并加上当前位置的值。如果两条路径重合,则只加一次。
  3. 输出结果

    • 最终的结果存储在 dp[N][N][N][N] 中,即两条路径都到达终点 (N-1, N-1) 的最大和。
时间复杂度分析
  • 由于四维数组的大小为 O(N^4),每个状态的更新需要常数时间,因此总的时间复杂度为 O(N^4)。对于 N ≤ 9 的情况,这个复杂度是可以接受的。
Logo

集算法之大成!助力oier实现梦想!

更多推荐