120. Triangle(中等)

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).

Note:
Bonus point if you are able to do this using only (O(n)) extra space, where n is the total number of rows in the triangle.

先说一个坑:本题绝不是找每行最小元素,然后把它们加起来那么简单.原因是这些元素是有路径的!看下例:

[
     [-1],
    [2, 3],
   [1,-1,-3],
]

结果应为 -1 + 2 + -1 = 0, 而不是 -1 + 2 + -3 = -2.

idea来自 http://www.cnblogs.com/liujinhong/p/5551932.html 中的图片,感谢这位同学.
本 code 属于 方法一: 自上而下,破坏原数组A. (O(n^2)) time, (O(1)) space
另一种方法 方法二: 自下而上,不破坏数组A. 维护一个长度为 n 的 1-dim 数组. (O(n^2)) time, (O(n)) space.

方法一的解题思路(请务必参照上面网址中的图片):

从上至下,将值按照"路径"加到下一层
除了A[0][0]这种情况外,还会遇到下列三种(注意判断条件)情况,共 4 cases:
	case 1: left, right 上邻居都存在
	case 2: left上不存在, right上存在
	case 3: left上存在, right上不存在
	case 4: A[0][0](它没left上 和 right上邻居), 我们 do nothing, 保留A[0][0]数值不变.

下面的图可以让我们看清上面 4 cases 的判断条件:

下面是元素索引:

	[
	     	  [0,0],
	       [1,0],[1,1] ,          [row-1,col-1],[row-1,col],
	    [2,0],[2,1],[2,2],                [row, col]
	 [3,0],[3,1],[3,2],[3,3]
	]

人家想法,自个代码(方法一,破坏原数组):
(O(n^2)) time, (O(1)) space

// idea来自 http://www.cnblogs.com/liujinhong/p/5551932.html
// 本 code 属于方法一:自上而下,破坏原数组A. $O(n^2)$ time, $O(1)$ space
// 另一种方法方法二:自下而上,不破坏数组A. 维护一个长度为 n 的 1-dim 数组.
//               $O(n^2)$ time, $O(n)$ space.
int minimumTotal(vector<vector<int>>& A) {
	const int n = A.size();
	if (n == 0) return 0;

	// 从上至下,将值按照"路径"加到下一层
	// 除了A[0][0]这种情况外,还会遇到下列三种情况,共 4 cases.
	for (int row = 0; row < n; row++) {
		for (int col = 0; col <= row; col++) {
			if ((row - 1 >= 0) && (col - 1 >= 0) && (col <= row - 1)) {
				// case 1: left, right 上邻居都存在
				int mi = min(A[row - 1][col - 1], A[row - 1][col]);
				A[row][col] += mi;
			} else if ((row - 1 >= 0) && (col - 1 < 0) && (col <= row - 1)) {
				// case 2: left上不存在, right上存在
				A[row][col] += A[row - 1][col];
			} else if ((row - 1 >= 0) && (col - 1 >= 0) && (col > row - 1)) {
				// case 3: left上存在, right上不存在
				A[row][col] += A[row - 1][col - 1];
			}
			// case 4: A[0][0](它没left上 和 right上邻居)
			// do nothing, 保留A[0][0]数值不变.
		}
	}

	// 返回A中最下面的一行(A[n-1])最小元素
	int res = INT_MAX;
	for (int i = 0; i < A[n - 1].size(); i++) {
		res = min(res, A[n - 1][i]);
	}
	return res;
}

方法二:
自下而上,不破坏数组A.
关键是找本层和上一层元素的关系,那就是 temp[j] = A[i,j] + min(temp[j], temp[j+1]).
(O(n^2)) time, (O(n)) space.

// 方法二:
// 自下而上,不破坏数组A. 维护一个长度为 n 的 1-dim 数组.
// $O(n^2)$ time, $O(n)$ space.
int minimumTotal(vector<vector<int>>& A) {
	const int n = A.size();
	if (n == 0)
		return 0;
	if (n == 1)
		return A[0][0];
	vector<int> temp;
	// A 最后一行存入temp
	for (int j = 0; j < n; j++)
		temp.push_back(A[n - 1][j]);

	// 从倒数第二行到上按路径元素取min的,相加
	// 对应关系:
	//     A[2, 1]
	// temp[1]  temp[1+1]

	for (int i = n - 2; i >= 0; i--) {
		for (int j = 0; j <= i; j++) {
			int smal = min(temp[j], temp[j + 1]);
			// 若当前使用temp[0], temp[1]
			// temp[0] 被改, 但不影响下次使用temp[1], temp[2]
			temp[j] = A[i][j] + smal;
		}
	}
	return temp[0];
}
原文地址:https://www.cnblogs.com/ZhongliangXiang/p/7450829.html