题目描述

观察下面的数字金字塔。

写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。

7 
      3   8 
    8   1   0 
  2   7   4   4 
4   5   2   6   5

在上面的样例中,从 初步动态规划讲解:数字三角形_c++

输入格式

第一个行一个正整数 初步动态规划讲解:数字三角形_动态规划_02

后面每行为这个数字金字塔特定行包含的整数。

输出格式

单独的一行,包含那个可能得到的最大的和。

样例 #1

样例输入 #1

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

样例输出 #1

30

提示

【数据范围】
对于 初步动态规划讲解:数字三角形_贪心算法_03 的数据,初步动态规划讲解:数字三角形_动态规划_04,所有输入在 初步动态规划讲解:数字三角形_最优解_05

我们们以样例为例:把这个三角形化为一个类似于二叉树的形式,也就是这样:

初步动态规划讲解:数字三角形_贪心算法_06


对于这道题,我们有两种思路可以走:

  • 思路一:
    从根节点出发,每次找一个(叶)子节点最大的方向去走。
    得到的最终答案:7 + 8 + 1 + 7 + 5 = 28
  • 思路二:
    从叶子结点出发,每次找一个和最大的方向(也就是每一层父节点与(叶)子节点之和最大的一条路)去走。
    得到的最终答案:5 + 7 + 8 + 3 + 8 = 30

第一种算法是大家熟悉的贪心算法,第二种则是动态规划(Dynamic Programming)算法。

我们可以发现:明显贪心算法所得的不是最优解。

这里就会体现动态规划与贪心算法的区别:如果我们每次都只用贪心找一个最大的数,我们最后得到的只是一个局部较优解,并非是我们最后要求的答案(也就是全局最优解);而动态规划算法则是先把大的问题划分成了类似的小问题,通过小问题的最优解的合并得到大问题的最优解。

把整棵树推完之后根结点即是最优解。

最后给上代码:

#include <iostream>
#define N 1005
using namespace std;
int n, a[N][N];
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= i; ++j) {
            scanf("%d", &a[i][j]);
        }
    }
    for (int i = n - 1; i > 0; --i) {
        for (int j = 1; j <= i; ++j) {
            a[i][j] += max(a[i + 1][j], a[i + 1][j + 1]);
        }
    }
    printf("%d", a[1][1]);
    return 0;
}