点击查看代码
/*
动态规划
第一种:通过记忆化搜素(先用递归的思想去解决问题;如果有重复子问题,就改成用记忆化搜索的形式)
(其实递推形式的动态规划中的各种表述,是可以对应到递归形式的:
• 状态表⽰ <---> 递归函数的意义;
• 状态转移⽅程 <---> 递归函数的主函数体;
• 初始化 <---> 递归函数的递归出⼝。)
第二种:直接使用递推形式的动态规划解决:
1.定义状态表示:
一般情况下根据经验+递归函数的意义,赋予dp数组相应的含义
2.推导状态转移方程:
根据状态表示以及题意,在dp表中分析,当前格子如何通过其余各自推导出来
3.初始化:
根据题意,先将显而易见的以及边界情况下的位置填上值
4.确定填表顺序:
根据状态转移方程,确定按照什么顺序来填表
5.确定最终结果
根据题意,在表中找出最终结果
*/
/*
题目:数字三角形
1.状态表示
根据题目要求
f[i][j]表示:从[1, 1] 走到 [i, j]位置时,所有方案下的最大权值
2.状态转移方程
根据“最后一步”划分情况
f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j]
3.初始化!!!
1.填表的时候不越界
2.保证后面的填表是正确的
思考:如果权值出现负数怎么办?
memset(f, -0x3f3f3f, sizeof(f)),f[0][0] = f[0][1] = 0
4.填表顺序
仅需保证从上到下就足矣
5.最终结果
最后一行的最大值
6.优化:空间优化
二维 -> 两个一维数组(一个表示当前需要计算的权值,另一个表示上次计算的权值) -> 一个一维数组
f[j] = max(f[j-1], f[j]) + a[i][j]
填表顺序:从左到右
总结:
二维转一维:
1.是否修改遍历顺序
2.删掉第一维即可
*/
//https://www.luogu.com.cn/problem/P1216
#include <iostream>
#include <cstring> // 用于memset函数
using namespace std;
const int N = 1010;
int a[N][N]; // 存储数字三角形的原始数值
//int f[N][N]; // dp表,f[i][j]表示从(1,1)到(i,j)的最大权值
int f[N]; //空间优化,二维 -> 一维
int main() {
int r;
cin >> r;
// 1. 输入数字三角形(从1,1开始存储,方便处理边界)
for (int i = 1; i <= r; i++) {
for (int j = 1; j <= i; j++) {
cin >> a[i][j];
}
}
// // 3. 填表(状态转移)
// // 填表顺序:从上到下,从左到右
// for (int i = 1; i <= r; i++) {
// for (int j = 1; j <= i; j++) {
// // 状态转移方程:当前位置的最大权值 = 上一行两个可能位置的最大值 + 当前值
// // 注意边界:j=1时只能从j=1来,j=i时只能从j=i-1来
// f[i][j] = max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j];
// }
// }
for(int i = 1; i <= r; i++){
for(int j = i; j >= 1; j--){ //要注意顺序,从左到右,不然会改变每一次的结果
f[j] = max(f[j - 1],f[j]) + a[i][j]; //当前f[j] 和左边 f[j - 1] 的max
}
}
// // 4. 找最终结果:最后一行的最大值
int ret = 0;
// for (int j = 1; j <= r; j++) {
// ret = max(ret, f[r][j]);
// }
for(int j = 1; j <= r; j++){
ret = max(ret, f[j]);
}
cout << ret << endl;
return 0;
}