问题描述:
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
释义:给定一个二维网络,以二维数组形式给出,数组大小为m X n, 从左上角(grid[0][0])到右下角(grid[m-1][n-1])最短路径,每次决策选择有两种:向右或向下。
问题分析:
1. 最后一次的决策取决于倒数第二次决策的两种选择中最短的那个决策,而这里两种决策又取决于它们上一次决策中的两种选择,如图所示:
2. 由此得到递推公式: f(i, j) = grid[i][j] + min(f(i-1, j), f(i, j-1));
3. 考虑边界条件: i==0 与 j==0 情况进行处理;
4. 递归深度对性能的影响。
实现:
i. 采用递归调用方式进行处理:
/*
* @brief: f(i, j) = grid[i][j] + min(f(i-1, j), f(i, j-1)); note find the formula*公式* first, then coding
* @authour: zhangjinxing
* @time: 2015 01 01 21 12 00
* @ edtion 1.0
*/
class Solution {
public:
int minPathSum(vector<vector<int> > &grid) {
if (0 == grid.size()) {
return -1;
}
if (0 == grid[0].size()) {
return -1;
}
int ret = 0;
ret = minPathSum(grid, grid.size()-1, grid[0].size()-1);
return ret;
}
private:
int minPathSum(vector<vector<int> > &grid, const unsigned int row, const unsigned int column) {
if (0 == row) { // special cases goes first (0, y)
int ret = 0;
for (unsigned int j=0; j<=column; ++j) {
ret += grid[row][j];
}
return ret;
}
if (0 == column) { // special cases goes first (x, 0)
int ret = 0;
for (unsigned int i=0; i<=row; ++i) {
ret += grid[i][column];
}
return ret;
}
return grid[row][column] + min(minPathSum(grid, row-1, column), minPathSum(grid, row, column-1));
}
};
分析
采用这种方式的优点是代码实现简单,但是存在一个问题就是由于递归调用造成了多次重复的计算,当数组达到一定的规模时,递归深度将很大,占用系统堆栈资源较多,会造成系统内存不够,而使得程序崩溃。
解决方案
用空间换时间!申请m X n维记录数组,用于记录每个节点(即决策)的最优解,避免不必要的重复计算。
ii. 采用记录数组替换递归调用:
/*
* @brief: f(i, j) = grid[i][j] + min(f(i-1, j), f(i, j-1)); note find the formula*公式* first, then coding
* @authour: zhangjinxing
* @time: 2015 01 01 21 12 00 modified time: 23:00
* @ edtion 1.0
*/
class Solution {
public:
int minPathSum(vector<vector<int> > &grid) {
if (0 == grid.size()) {
return -1;
}
if (0 == grid[0].size()) {
return -1;
}
const unsigned int m = grid.size(), n = grid[0].size();
// using extra space to record the decision instead of the recursion *递归*
vector<vector<int> > arr(m, vector<int>(n, INT_MAX));// INT_MAX #include <limits.h>
unsigned int i, j;
for (i=0; i<m; ++i) {
for (j=0; j<n; ++j) {
if (0!=i && 0!=j && INT_MAX != arr[i-1][j] && INT_MAX!=arr[i][j-1]) {
arr[i][j] = 0;
arr[i][j] = grid[i][j] + min(arr[i-1][j], arr[i][j-1]);
} else if (0 == i && INT_MAX == arr[i][j]) {
arr[i][j] = 0;
for(unsigned int k=0; k<=j; ++k) {
arr[i][j] += grid[i][k];
}
} else if (0 == j && INT_MAX == arr[i][j]) {
arr[i][j] = 0;
for(unsigned int k=0; k<=i; ++k) {
arr[i][j] += grid[k][j];
}
}
}
}
int ret = arr[m-1][n-1];
return ret;
}
};
总结
i. using recurision method may exist the problem of calling too many times of the basic funciton, it may lead Time Limit Exceeded & Repeat Computing;
ii. take advantage of the record variables to instead the recurision;
iii. think before you act;
iv. special cases comes first and just "code" it.
-----One "Acceptance" a day, keep my mind awake.-----noted by bayingbf-----2015.01.02-----