Loading

初步动态规划讲解:数字三角形

题目描述

观察下面的数字金字塔。

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

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

在上面的样例中,从 7 → 3 → 8 → 7 → 5 7 \to 3 \to 8 \to 7 \to 5 73875 的路径产生了最大

输入格式

第一个行一个正整数 r r r ,表示行的数目。

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

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

30

提示

【数据范围】
对于 100 % 100\% 100% 的数据, 1 ≤ r ≤ 1000 1\le r \le 1000 1r1000,所有输入在 [ 0 , 100 ] [0,100] [0,100] 范围内。

我们们以样例为例:把这个三角形化为一个类似于二叉树的形式,也就是这样:
在这里插入图片描述
对于这道题,我们有两种思路可以走:

  • 思路一:
    从根节点出发,每次找一个(叶)子节点最大的方向去走。
    得到的最终答案: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;
}
posted @ 2023-05-05 11:26  popcoount  阅读(18)  评论(0编辑  收藏  举报  来源