SPOJ MARTIAN 题解

思路

首先简单理解一下题目,会发现题目就是在网格上选择若干列(或列的上面一段)以及若干行(或行的左边一段)似的这些被选中的列和行之间的和最大。具体可以看题目的这张图:

很明显这是一个 DP 问题,于是我们着手设计状态转移方程。首先我们先分别预处理出纵向的和横向的两个前缀和分别是 \(y_{i,\,j}\)\(b_{i,\,j}\),字母对应题目的矿物名称首字母。然后我们设状态 \(f_{i,\,j}\) 表示从这个位置开始到左上角的答案(最多的采矿数量和)。然后我们发现状态 \(f_{i,\,j}\) 只与 \(f_{i - 1, j}\)\(f_{i,\, j - 1}\) 有关。

稍加思考即可得出我们的状态转移方程: \[ f_{i,\,j} = \max\left(f_{i - 1,\,j} + b_{i,\,j},\, f_{i,\,j - 1} + y_{i,\,j}\right) \] 于是就可以快乐 DP 了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <cstring>

using namespace std;

int n, m, y[509][509], b[509][509], f[509][509];

signed main() {
while (cin >> n >> m) {
if (n == 0 && m == 0) {
return 0;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> b[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> y[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
b[i][j] += b[i][j - 1];
y[i][j] += y[i - 1][j];
}
}
memset(f, 0, sizeof f);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
f[i][j] = max(f[i - 1][j] + b[i][j], f[i][j - 1] + y[i][j]);
}
}
cout << f[n][m] << endl;
}
}

SPOJ MARTIAN 题解
https://lixuannan.github.io/posts/57531
作者
CodingCow Lee
发布于
2024年6月4日
许可协议