两条等长路径按反对角线同步推进,状态 (k, r₁, r₂) 消去列维、同格收益只计一份,O(N³) 滚动 DP 求最大取数和。
OJ: roj
题目 ID: 3503
难度:普及+/提高-
标签:动态规划python
创建: 2026-10-02 04:16
更新: 2026-10-10 11:30
形式化题目
给定
- 关键约束:两条路径踩到同一格时,该格的数只计一次(因为已被取走)。
- 数据范围:
(真实数据出现过 ),权值为正整数。
样例的最优方案如下表:① 标记第一条路径,② 标记第二条路径,格子显示权值。第一条路径取到
| 行\列 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| 1 | ①② | ①② | ① | 0 | 0 | 0 | 0 | 0 |
| 2 | 0 | ② | 13 ① | ① | 0 | 6 | 0 | 0 |
| 3 | 0 | ② | 0 | ① | 7 | 0 | 0 | 0 |
| 4 | 0 | ② | 0 | 14 ① | ① | ① | 0 | 0 |
| 5 | 0 | 21 ② | ② | 0 | 0 | 4 ① | ① | ① |
| 6 | 0 | 0 | 15 ② | ② | ② | ② | ② | ①② |
| 7 | 0 | 14 | 0 | 0 | 0 | 0 | 0 | ①② |
| 8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ①② |
注意第 7 行的 14 不在任何一条最优路径上——单条路径“路过最多”不等于两条路径“合起来取最多”,选路必须把两条路径放在一起权衡。
正解
思路
朴素做法与其瓶颈。 最直接的想法是枚举路径:
关键观察:两条路径等长,可以按步同步。 无论怎么走,每条路径都恰好走
其中
转移。 每人上一步只能来自"正上方"(行
两人同在第
小规模演示。 用真实数据 fgqs0(
| 10 | — | — | — | — | — | — | — | — | |
| 10 | 10 | — | 10 | 10 | — | — | — | — | |
| 15 | 21 | 23 | 21 | 16 | 24 | 23 | 24 | 18 | |
| — | — | — | — | 25 | 30 | — | 30 | 26 | |
| — | — | — | — | — | — | — | — | 30 |
读表要点:
实现要点。 用 dict[(r1, r2)] 存当前层(天然跳过不可达状态),4 前驱用 cur.get((p1, p2), NEG) 取 max、NEG 哨兵挡掉不可达;lo, hi = max(1, k-n), min(n, k-1) 截断行号保证列不越界;r == 0 的行是输入结束标记。
代码
cur 是滚动的 nxt 是 gain 的三元表达式实现"同格只计一份";内层生成器一次算出 4 个前驱的最大值。
/**
* 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-10 11:30
* update_at: 2026-10-10 11:30
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NEG = -1000000000;
int n;
int grid[22][22];
// cur[r1][r2]:两人同在斜线 k = 行+列 上、分别位于 (r1, k-r1)、(r2, k-r2) 时的最大和
int cur[22][22], nxt[22][22];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
while (true) {
int r, c, v;
cin >> r >> c >> v;
if (r == 0 && c == 0 && v == 0) break;
grid[r][c] = v;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cur[i][j] = NEG;
cur[1][1] = grid[1][1];
for (int k = 3; k <= 2 * n; k++) {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
nxt[i][j] = NEG;
int lo = max(1, k - n), hi = min(n, k - 1);
for (int r1 = lo; r1 <= hi; r1++) {
int g1 = grid[r1][k - r1];
for (int r2 = lo; r2 <= hi; r2++) {
int gain = (r1 == r2) ? g1 : g1 + grid[r2][k - r2];
int best = NEG;
for (int p1 = r1 - 1; p1 <= r1; p1++)
for (int p2 = r2 - 1; p2 <= r2; p2++)
if (p1 >= 1 && p2 >= 1 && cur[p1][p2] > best)
best = cur[p1][p2];
if (best > NEG) nxt[r1][r2] = best + gain;
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cur[i][j] = nxt[i][j];
}
cout << cur[n][n] << "\n";
return 0;
}#!/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-10-02 04:16
# update_at: 2026-10-04 13:17
import sys
NEG = -10**9 # 不可达状态哨兵(权值全为正,真实答案恒 >= 0)
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
# 1-indexed 方格,四周留 0 边界,未填数的格子自然为 0
grid = [[0] * (n + 2) for _ in range(n + 2)]
for r, c, v in zip(data, data, data):
if r == 0: # "0 0 0" 是输入结束标记
break
grid[r][c] = v
# cur[(r1, r2)]:两人同在斜线 k = 行+列 上、分别位于 (r1, k-r1)、(r2, k-r2) 时
# 已取得的最大和;同格被两人同时踩到时只计一次(取过的格子变 0)
cur = {(1, 1): grid[1][1]} # 两条路径都从 A 出发
for k in range(3, 2 * n + 1): # 下一条斜线(A 点所在斜线 k=2 已定)
nxt = {}
lo, hi = max(1, k - n), min(n, k - 1) # 行号合法区间:列 = k-行 也在 1..n
for r1 in range(lo, hi + 1):
g1 = grid[r1][k - r1]
for r2 in range(lo, hi + 1):
# 两人不同格各取一份,同格只取一份
gain = g1 if r1 == r2 else g1 + grid[r2][k - r2]
# 上一步两人各自只能从"正上方(行-1)"或"正左方(行不变)"走来
best = max(cur.get((p1, p2), NEG) for p1 in (r1 - 1, r1) for p2 in (r2 - 1, r2))
if best > NEG: # 至少有一个前驱可达
nxt[r1, r2] = best + gain
cur = nxt
print(cur[n, n]) # 终点 B 所在斜线上只剩 (n, n) 一个状态
if __name__ == "__main__":
solve()复杂度
- 时间复杂度:
: 共 层,每层 各 种,每状态 4 次字典查找。对照枚举路径对的约 ( 时 级)是决定性改进。 - 空间复杂度:
:滚动保存两层状态,每层最多 个 tuple键。
总结
本题是"网格 DAG 上的双路径最大点权和"。核心两步:
- 等步长消维——两条路径都恰走
步,第 步必同在 上,四维状态压成 ,并把 做成滚动维,空间降到 。 - 联合决策 + 同格合并——两条路径必须同时推进(先后贪心会互相抢格子),转移时用
判断同一格、收益只加一份,精确模拟"取过的格子变 0"。
两个易错点:起点和终点被两条路径共享,只应计一次(初值只写 max 会把垃圾状态当合法前驱。
实测 verify.sh 3503:6/6 真实数据(含
