动态规划数字三角形模型
定义
动态规划数字三角形模型是在一个三角形的数阵中,通过一定规则找到从顶部到底部的最优路径或最优值。
运用情况
通常用于解决具有递推关系、需要在不同路径中做出选择以达到最优结果的问题。比如计算最短路径、最大和等。
计算其他类型的最优值。比如,要求找到从顶部到底部路径上数字乘积最大,或者找到具有某种特定属性(如奇数个数最多等)的最优路径。
注意事项
- 状态定义的准确性:要仔细分析问题,选择最能简洁且准确反映问题本质的状态表示。如果定义不当,可能导致后续递推关系复杂或无法正确求解。
- 边界条件的多样性:不同问题的边界条件可能不同,比如三角形顶部的状态初始化,或者边缘位置的特殊处理等。
- 递推关系的严谨性:需要充分考虑各种可能情况,确保递推关系涵盖了所有可能的决策和状态转移。
解题思路
- 在确定状态时,有时可能需要结合一些额外的信息,比如记录路径本身或其他相关属性。
- 递推关系的建立可能需要综合考虑多个因素,甚至可能引入辅助数组或变量来辅助计算。
- 对于复杂的数字三角形问题,可能需要分阶段或分层进行递推计算,逐步逼近最终的最优解。
例如,在一个更复杂的数字三角形中,除了数字本身,每个位置还有一个权重,要求在权重限制下找到最大和。这时状态可能需要扩展为 dp[i][j][k] 表示到达第 i 行第 j 列且当前权重为 k 时的最大和,递推关系也会相应变得更加复杂。通过这样的细致分析和设计,可以更好地运用动态规划数字三角形模型解决各种实际问题。
AcWing 275. 传纸条
题目描述
275. 传纸条 - AcWing题库
运行代码
#include <iostream>
#include <cstring>
using namespace std;
int w[60][60], f[60*60][60][60];
void run()
{
int n, m;
cin >> n >> m;
int a, b, c;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
cin >> w[i][j];
for(int k = 2; k <= n + m; k ++)
for(int i1 = max(1, k - m); i1 <= n && i1 < k; i1 ++)
for(int i2 = max(1, k - m); i2 <= n && i2 < k; i2 ++)
{
int j1 = k - i1, j2 = k - i2;
if(j1 >= 1 && j1 <= m && j2 >= 1 && j2 <= m)
{
int t = w[i1][j1];
if(i1 != i2) t += w[i2][j2];
int &x = f[k][i1][i2];
x = max(x, f[k - 1][i1][i2] + t);
x = max(x, f[k - 1][i1 - 1][i2] + t);
x = max(x, f[k - 1][i1][i2 - 1] + t);
x = max(x, f[k - 1][i1 - 1][i2 - 1] + t);
}
}
cout << f[n + m][n][n] << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
run();
return 0;
}
代码思路
- 定义了二维数组
w
来存储每个位置的好感度值。 f[k][i1][i2]
这个三维数组用于记录在特定阶段(即走了k
步时),从左上角到点(i1, j1)
和从左上角到点(i2, j2)
(其中j1 = k - i1
,j2 = k - i2
)两条路径所能获得的最大好感度和。- 通过三重循环遍历不同的位置和阶段。对于每个阶段
k
和对应的两个位置i1
、i2
,计算当前状态下能获得的好感度值,并与之前的状态进行比较更新。具体更新时,考虑从四个可能的方向(上一步是i1
不变i2
不变、i1
减 1i2
不变、i1
不变i2
减 1、i1
减 1i2
也减 1)转移过来的情况,取最大值进行更新。 - 最后输出
f[n + m][n][n]
,即走完整个矩阵从左上角到右下角且两条路径都到达右下角时的最大好感度和。
改进思路
- 可以考虑添加一些必要的注释提高代码的可读性。
- 对于一些重复的代码逻辑,可以考虑提取成函数来提高代码的简洁性和可维护性。
改进代码
#include <iostream>
#include <cstring>
using namespace std;
int w[60][60], f[60*60][60][60];
void updateMax(int k, int i1, int i2, int j1, int j2, int t) {
int& x = f[k][i1][i2];
x = max(x, f[k - 1][i1][i2] + t);
x = max(x, f[k - 1][i1 - 1][i2] + t);
x = max(x, f[k - 1][i1][i2 - 1] + t);
x = max(x, f[k - 1][i1 - 1][i2 - 1] + t);
}
void run() {
int n, m;
cin >> n >> m;
int a, b, c;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
cin >> w[i][j];
for(int k = 2; k <= n + m; k ++) {
for(int i1 = max(1, k - m); i1 <= n && i1 < k; i1 ++) {
for(int i2 = max(1, k - m); i2 <= n && i2 < k; i2 ++) {
int j1 = k - i1, j2 = k - i2;
if(j1 >= 1 && j1 <= m && j2 >= 1 && j2 <= m) {
int t = w[i1][j1];
if(i1!= i2) t += w[i2][j2];
updateMax(k, i1, i2, j1, j2, t);
}
}
}
}
cout << f[n + m][n][n] << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
run();
return 0;
}
其它代码
#include <iostream>
using namespace std;
const int N = 60;
int n, m;
int a[N][N];
int f[N * 2][N][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
cin >> a[i][j];
for(int k = 2; k <= n + m; k ++ )
for(int x1 = 1; x1 <= n; x1 ++ )
for(int x2 = 1; x2 <= n; x2 ++ )
{
int y1 = k - x1, y2 = k - x2;
int t = a[x1][y1];
if(y1 >= 1 && y1 <= m && y2 >= 1 && y2 <= m)
{
if(x1 != x2 || k == 2 || k == n + m)
{
t += a[x2][y2];
int &c = f[k][x1][x2];
c = max(c, f[k - 1][x1][x2] + t);
c = max(c, f[k - 1][x1 - 1][x2] + t);
c = max(c, f[k - 1][x1][x2 - 1] + t);
c = max(c, f[k - 1][x1 - 1][x2 - 1] + t);
}
}
}
cout << f[n + m][n][n] << endl;
return 0;
}
代码思路
- 定义了常量
N
表示矩阵的最大规模。 - 输入矩阵的行数
n
和列数m
,并读取矩阵元素值到a
数组。 f[k][x1][x2]
这个三维数组用于记录某种状态下的最优值。- 通过三重循环遍历不同的阶段(由
k
表示)以及两个位置x1
和x2
。根据当前的行坐标计算出对应的列坐标y1
和y2
。 - 只有当两个位置对应的列坐标都在合法范围内时,进行计算。如果满足特定条件(比如两个位置不同或者是边界情况),计算当前的好感度值
t
(包含两个位置的元素值),然后更新f[k][x1][x2]
的值,通过与之前阶段的各种可能情况的最优值进行比较取最大值。 - 最后输出最终状态下
f[n+m][n][n]
的值。