您的位置:首页 > 其它

算法课第11周第1题——120. Triangle

2017-05-07 20:31 218 查看
题目描述:

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

For example, given the following triangle

[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]


The minimum path sum from top to bottom is 
11
 (i.e., 2 + 3 + 5 + 1 =
11).

程序代码:

class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
// 计算三角形层数
int n = triangle.size();
// 从底端开始往上计算,避免考虑两边的多余处理,并简化计算
// f[j]表示第j列处的最小和。因为从下往上计算,因此不需要二维数组,节约了空间(类似用一维数组做背包问题)
// f数组初始赋值取三角形最下层
vector<int> f(triangle[n - 1]);

// 往上计算,取该处triangle值加上下一层的左下或右下的值
for (int i = n - 2; i >= 0; i--) {
for (int j = 0; j <= triangle[i].size(); j++) {
f[j] = min(f[j] + triangle[i][j], f[j + 1] + triangle[i][j]);
}
}

// 往上计算到最后,只需要返回f[0]的值
return f[0];
}
};

简要题解:

本题使用了动态规划算法。

先理清题意。本题所给的输入是一个由数字构成的三角形(表示为一个二维数组)。需要求出该三角形中连接顶端和底端的路径中最大的数字总和。

思考该题时,我一开始是考虑从顶端开始计算,可以参考下图:



这样的话,可以通过使用一个二层循环枚举i = 0 ~ n-1, j = 0~triangle[i].size, 得到转移方程:

g[i][j] = min {g[i-1][j-1] + triangle[i][j], g[i-1][j] + triangle[i][j] }.  而最终的输出则是min{ g[n-1][j],  j从0到triangle[n-1].size()}

但是,用这个从顶端到底端的计算方法,有明显的缺陷。首先,转移方程并不完全正确,因为对于某一行两侧的数字(如上图中第三行的4和6),就需要做分类的讨论(因为某行最左侧的数字的左上方没有数字,最右侧数字的右上方没有数字),某行最左端处只能取g[i][j] = g[i-1][j] + triangle[i][j], 而最右端只能取g[i][j] = g[i-1][j] + triangle[i][j], 这样分类讨论起来就麻烦不少; 第二,这样计算时最后一步计算最终输出还需要对n-1行做一个循环,若该行中数字很多,可能会大大增加计算时间;第三,这样运算需要再用到一个二维数组g[i][j],对空间的利用也相对没太高的效率。

为了解决这些问题,我经过思考,联想到背包问题中用一维数组代替二维数组的方法,考虑改变策略,从底端到顶端来计算。这样,就不需要一个二维数组,而只需要一个一维数组f[j],其初始化为三角形最底端的一行(即triangle[n-1]). 通过枚举i = n-2 ~ 0,  j = 0 ~ triangle[i].size(),
可以列出转移方程:

f[j] = min(f[j] + triangle[i][j], f[j + 1] + triangle[i][j])

这样从底端到顶端计算的方法,同时还解决了需要分类讨论某行两侧数字的问题,而只要统一考虑下方一层的左下和右下两个数字。此外,这样计算,最终的输出就是f[0], 而不像从顶到底那样需要最后还需要做一次循环。

通过本题,我意识到,即使是动态规划,也可以考虑多种策略,例如本题的从顶到底或从底到顶两种策略,不同动态规划的策略时间和空间效率可能会差很多。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: