[NOIP2000 提高组] 方格取数 (2-2)

145 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情

P1004 [NOIP2000 提高组] 方格取数 - 洛谷

题目描述

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

A
 0  0  0  0  0  0  0  0
 0  0 13  0  0  6  0  0
 0  0  0  0  7  0  0  0
 0  0  0 14  0  0  0  0
 0 21  0  0  0  4  0  0
 0  0 15  0  0  0  0  0
 0 14  0  0  0  0  0  0
 0  0  0  0  0  0  0  0
                         B

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

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

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

input

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

output

67

题目分析

首先我们考虑只走一次的情况,这是基础的线性dp,可用数字三角形模型解决。

那么我们是否可以按这样的思路两次dp呢,答案是否定的,反例如下。

Screenshot_20230203_120934_Samsung Notes.jpg

在第一次dp后会按照蓝线行走,这样的话便无法将剩下的位置完全遍历,而通过观察我们知道两次行走是可以走尽所有数的。

则我们需要构造另一种dp方式。

我们假设两次行走同时发生,f[i1][j1][i2][j2]f[i_1][j_1][i_2][j_2] 表示当前两个点位所处的位置和取数最大和。

考虑转移,每个人可由两个方向转移而来,共四种情况。

答案输出 f[n][n][n][n]f[n][n][n][n] 即可,时间复杂度为 O(n4)O(n^4)

Accept代码

#include <bits/stdc++.h>

using namespace std;

const int N = 11;
int g[N][N], f[N][N][N][N];

int main()
{
    int n; cin >> n;
    int x, y, v;
    while (cin >> x >> y >> v, x) g[x][y] = v;
    for (int i1 = 1; i1 <= n; i1 ++)
        for (int j1 = 1; j1 <= n; j1 ++)
            for (int i2 = 1; i2 <= n; i2 ++)
                for (int j2 = 1; j2 <= n; j2 ++)
                {
                    int v = g[i1][j1] + (i1 == i2 && j1 == j2 ? 0 : g[i2][j2]);
                    int &x = f[i1][j1][i2][j2];
                    x = max(x, f[i1 - 1][j1][i2 - 1][j2]);
                    x = max(x, f[i1][j1 - 1][i2 - 1][j2]);
                    x = max(x, f[i1][j1 - 1][i2][j2 - 1]);
                    x = max(x, f[i1 - 1][j1][i2][j2 - 1]);
                    x += v;
                }
        
    cout << f[n][n][n][n] << "\n";
    return 0;
}