P7074 [CSP-J 2020] 方格取数
思路来自大佬:Indjy
学校老师居然把这个题放在区间DP里面但是我没想到该怎样用,标签里也没有,那就用暴力DP来做吧。
题目大意
有一个\(n \times m\)的方格,可以向下,向上,向右走一格,而且不能走重复的格子。
现在要求在一条路径中和最大的走法。
解题思路
因为这个题目比较扯,是向上、向下、向右走,不是我们常见的向下、向左、向右,因此我们想到了一种神奇的方法:
将整个表格顺时针旋转90度。
这下就好办了:变成了向下、向左、向右的走法了。
看到这个走法你会想到什么?是不是感觉很熟悉?
没错,那就是数字三角形的走法。因此,这一道普及+/提高-的题一下子变成了普及-。
需要的数组及变量
dp[N][N][2]
:顾名思义就是存动态规划。但是,和数字三角形不一样,我们需要多一维来存是向左还是向右。
a[N][N]
:存原来的数据(表格)。
n
,m
:题目所给。
设计代码
DP方程
我们既然已经知道了DP方程和数字三角形很类似,那就直接上吧。
我们设\(dp[i][j][0 / 1]\)表示现在的位置是\((i, j)\),0表示向左,1表示向右。
向下走
向下走和之前来的方向没有关系,所以就是看左边还是右边最大然后直接转移。那么方程就是:
\(dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1]) + a[i][j]\),
\(dp[i][j][1] = max(dp[i - 1][j][0], dp[i - 1][j][1]) + a[i][j]\)。
向左走
顾名思义,就是看当前的大还是从左边转移的大。于是方程就是:
\(dp[i][j][0] = max(dp[i][j][0], dp[i][j - 1][0] + a[i][j])\)。
向右走
和向左走一样,方程就是:
\(dp[i][j][1] = max(dp[i][j][1], dp[i][j + 1][1] + a[i][j])\)。
两个方向要分别转移,向左要倒序,向右要正序。
完整代码
#include <bits/stdc++.h>
#define int long long
#define inf 0x7f7f7f7f7f7f7f
using namespace std;
const int N = 1e3 + 10;
int dp[N][N][2], a[N][N];
int n, m;
int read() {
int x = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * w;
}
signed main() {
n = read(); m = read();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[j][i] = read();
swap(n, m);
memset(dp, -inf, sizeof dp);
dp[1][1][0] = dp[1][1][1] = a[1][1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (i != 1) {
dp[i][j][0] = max(dp[i][j][0], max(dp[i - 1][j][0], dp[i - 1][j][1]) + a[i][j]);
dp[i][j][1] = max(dp[i][j][1], max(dp[i - 1][j][0], dp[i - 1][j][1]) + a[i][j]);
}
if (j != 1)
dp[i][j][0] = max(dp[i][j][0], dp[i][j - 1][0] + a[i][j]);
}
for (int j = m; j >= 1; j--)
if (j != m)
dp[i][j][1] = max(dp[i][j][1], dp[i][j + 1][1] + a[i][j]);
}
cout << max(dp[n][m][0], dp[n][m][1]) << endl;
return 0;
}
给个赞吧!