初步动态规划讲解:数字三角形
题目描述
观察下面的数字金字塔。
写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。
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 7→3→8→7→5 的路径产生了最大
输入格式
第一个行一个正整数 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
1≤r≤1000,所有输入在
[
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;
}