网格只能向东、向南走,到达每格的最大收获只由上方与左方两格决定,线性 DP 一维滚动数组 O(RC) 直接推进到东南角。
OJ: roj
题目 ID: 1284
难度:入门
标签:动态规划网格 DP线性 DP
创建: 2026-09-30 03:14
更新: 2026-10-05 08:04
题目描述
Hello Kitty 想摘点花生送给她喜欢的米老鼠。她来到一片
样例:输入 1 1 和 3 4,输出 8;第二组 2 3 4 和 1 6 5,输出 16(走
思路
由于只能向东、向南走,到达格子
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 03:16
# update_at: 2026-09-30 03:16
import sys
from collections.abc import Iterator
def pick_max(grid: list[list[int]]) -> int:
"""从西北角走到东南角、只向东南走,最多能摘到的花生数。
逐行原地滚动:更新到 (r, c) 时,row_dp[c] 还是上一行(来自上方)的答案,
row_dp[c-1] 已是本行(来自左方)的答案,取较大者加上当前格的花生即可。
"""
width = len(grid[0])
row_dp = [0] * width # 第 -1 行(虚拟):网格外收获为 0
for row in grid:
for c in range(width):
from_left = row_dp[c - 1] if c else 0
row_dp[c] = row[c] + max(row_dp[c], from_left)
return row_dp[-1]
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
out: list[str] = []
T = next(data)
for _ in range(T):
R, C = next(data), next(data)
grid = [[next(data) for _ in range(C)] for _ in range(R)]
out.append(str(pick_max(grid)))
print('\n'.join(out))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 08:04
* update_at: 2026-10-05 08:04
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 105;
typedef long long ll;
ll T; // 数据组数
ll R, C; // 花生地的行数、列数
ll m[MAXN][MAXN]; // m[i][j]:格子 (i, j) 上的花生数
ll dp[MAXN]; // 一维滚动数组:更新到 dp[j] 前,dp[j] 是上一行的答案(来自上方),dp[j-1] 是本行左边的答案
// 读取一组花生地,并把 dp 数组清成"虚拟第 0 行"(全 0,网格外收获为 0)。
void read_input() {
cin >> R >> C;
for (ll i = 1; i <= R; i++) {
for (ll j = 1; j <= C; j++) {
cin >> m[i][j];
}
}
for (ll j = 1; j <= C; j++) dp[j] = 0;
}
// 网格 DP:只能向东、向南走,dp[i][j] = m[i][j] + max(上方 dp, 左方 dp)。
// 首行只能从左方来、首列只能从上方来,边界全 0 后转移式自动覆盖,不用单独处理。
void solve() {
for (ll i = 1; i <= R; i++) {
for (ll j = 1; j <= C; j++) {
ll best = dp[j]; // 来自上方
if (j > 1 && dp[j - 1] > best) best = dp[j - 1]; // 来自左方
dp[j] = best + m[i][j];
}
}
cout << dp[C] << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> T;
while (T--) {
read_input();
solve();
}
return 0;
}