2N-1 时限等价于只能向右/向下,网格 DP 求最小费用,越界来源按 INF 处理。
OJ: acwing
题目 ID: 1018
难度:普及-
标签:动态规划网格DPc++
日期: 2026-08-04 12:50
题意
思路
直接枚举所有路径是
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-08-04 12:50
* update_at: 2026-08-04 12:50
*/
// brute.cpp:小数据暴力解,使用选择序列递归枚举所有路径。
// 每一层递归在选择"下一步向右还是向下走",走到底(到达 (n,n))时结算并更新最优。
// 只能处理小数据:路径数是组合数 C(2n-2, n-1),指数级。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 10;
const int INF = 0x3f3f3f3f;
int n; // 网格边长
int a[MAXN][MAXN]; // 费用
int ans; // 最小费用
// 当前在 (x, y),已花费 sum
void dfs(int x, int y, int sum) {
if (x == n && y == n) { // 到达右下角,结算
ans = min(ans, sum);
return;
}
if (x < n) dfs(x + 1, y, sum + a[x + 1][y]); // 向下走
if (y < n) dfs(x, y + 1, sum + a[x][y + 1]); // 向右走
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> a[i][j];
}
}
ans = INF;
dfs(1, 1, a[1][1]);
cout << ans << '\n';
return 0;
}这个暴力把路线看成选择序列:每一层递归在选择"下一步向右还是向下",到达
关键观察:只能向右/向下走,每个格子
定义
边界
以样例左上 [1 4 6; 2 5 7; 6 8 9])走一遍转移表:
| i \ j | 1 | 2 | 3 |
|---|---|---|---|
| 1 | 1 | 1+4=5 | 5+6=11 |
| 2 | 1+2=3 | min(3,5)+5=8 | min(8,11)+7=15 |
| 3 | 3+6=9 | min(9,8)+8=16 | min(16,15)+9=24 |
看第 3 行第 3 列:
代码
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-08-04 12:50
* update_at: 2026-08-04 12:50
*/
/* AcWing 1018 最低通行费 */
/* 2N-1 步穿越 N×N 网格 ⇔ 只能向右/向下走(无绕路余地),
* 网格 DP 求最小费用:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + a[i][j]。 */
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
int n; // 网格边长
int a[MAXN][MAXN]; // a[i][j]:格子 (i,j) 的费用
int dp[MAXN][MAXN]; // dp[i][j]:从 (1,1) 到 (i,j) 的最小费用
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> a[i][j];
}
}
// 起点边界;其余格子先置 INF,保证越界来源(dp[0][j]、dp[i][0])不会被选中
memset(dp, 0x3f, sizeof(dp));
dp[1][1] = a[1][1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == 1 && j == 1)
continue;
// 从上方或左方走来,取费用较小者(越界来源是 INF,自然被排除)
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + a[i][j];
}
}
cout << dp[n][n] << '\n';
return 0;
}复杂度
总结
和摘花生(1015)是同一个网格路径 DP 模型,只差两个字母:max → min。但这一换就引出新坑——最小版必须把越界来源初始化为 INF,否则全局数组的 0 会被当成合法来源。记住这个对比,两个题一起学效果最好。