文章目录
0. 前言
相关:
1. 数字三角形+模板题
理解:
就是要求了只能向右、下走。那么就完全等价于[线性dp] 摘花生(数字三角形模型)了。
重点: 线性 dp
、数字三角形模型
思路:
-
状态定义:
f[i][j]
表示从起点[1,1]
走到[i,j]
点的所有路径和的最大值
-
状态转移:
- 来自左上方:
f[i-1][j] + a[i][j]
- 来自右上方:
f[i][j-1] + a[i][j]
- 则状态转移方程为:
f[i][j] = max(f[i-1][j], f[i][j-1]) + a[i][j]
- 来自左上方:
-
边界情况及初始化:
- 本题尤其注意
f
数组初始化问题,由于是求min
,所以f
最好统一初始化为0x3f3f3f3f
,但由于状态转移是从左上角开始,第一行不能从 0 行进行转移,第一列不能从 0 列开始转移。可以进行特判一下第一行。对应写法一,这样写代码比较复杂,但是逻辑相当清楚 - 也可直接对
f
数组进行初始化为0x3f3f3f3f
,然后针对f[0][0] = f[0][1] = f[1][0] = 0
进行初始化赋值,这样就能有效从左上角进行状态转移,精简代码。对应写法二 - 答案即为:
f[n][n]
- 本题尤其注意
本题求 min
,一定考虑清楚状态的初始化,最为重要!
写法一代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 105, INF = 1e9;
int n;
int w[N][N];
int f[N][N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
scanf("%d", &w[i][j]);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (i == 1 && j == 1) f[i][j] = w[i][j]; // 特判左上角
else {
f[i][j] = INF;
if (i > 1) f[i][j] = min(f[i][j], f[i - 1][j] + w[i][j]); // 不在第一行,才可以从上面过来
if (j > 1) f[i][j] = min(f[i][j], f[i][j - 1] + w[i][j]); // 不在第一列,才可以从左边过来
}
printf("%d", f[n][n]);
return 0;
}
写法二代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 105, INF = 1e9;
int n;
int w[N][N];
int f[N][N];
int main() {
memset(f, 0x3f, sizeof f);
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
scanf("%d", &w[i][j]);
f[0][0] = f[1][0] = f[0][1] = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
f[i][j] = min(f[i][j - 1], f[i - 1][j]) + w[i][j];
printf("%d", f[n][n]);
return 0;
}