文章目录
题意简述
题目链接
Vijos ETO P1001(推荐)
Acwing 898
Luogu P1216
题目描述
给定一个的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
输入输出样例
输入样例
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出样例
30
样例解释
在上面的样例中,从 7 → 3 → 8 → 7 → 5 7→3→8→7→5 7→3→8→7→5 的路径产生了最大权值。
思路分析
贪心?
首先考虑是否可以贪心。
路径是不是需要经过最大值?
容易构造一组反例。
输入:
3
1
2 100
99 0 0
如果经过最大值 100 100 100,那么最终结果为 101 101 101;但是如果不这样走,通过路径 1 → 2 → 99 1\to2\to99 1→2→99,那么结果为 102 102 102,显然 102 > 101 102>101 102>101,所以这种贪心方法并不正确。
如果转而考虑次大值,次次大值,那么会在贪心的错误算法中越陷越深。此时,我们需要使用过其他的算法。
递归版动态规划(记忆化搜索)
爆搜
爆搜思路
既然贪心行不通,那么似乎没有什么好办法了,那么,我们可以考虑进行搜索,然后进行剪枝等优化。
爆搜代码
#include <iostream>
const int MAXN = 1e3 + 10;
int n, a[MAXN][MAXN];
int dfs(int x, int y) {
if (x == n) return a[x][y]; // 边界条件
return a[x][y] + std::max(dfs(x + 1, y), dfs(x + 1, y + 1));
// 选择下一层的更大的计算出来的值加上当前值
}
int main() {
std::cin >> n;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= i; ++j)
std::cin >> a[i][j];
std::cout << dfs(1, 1) << std::endl;
return 0;
}
当然,上面的代码可以等价变形为如下代码:
#include <iostream>
const int MAXN = 1e3 + 10;
int n, a[MAXN][MAXN];
int dfs(int x, int y) {
return a[x][y] + (x == n ? 0 : std::max(dfs(x + 1, y), dfs(x + 1, y + 1)))