DP都是bottom up的,也有人用top down + cache的做法, top down就是递归地去做(当然你也可以用循环写,然后自己维护栈)。要想使用DP,一定要有最优子结构性质。
此外,如果求的是从最优解算出的最优值,特别适合DP,但如果还要求最优解,那么就得有备忘录了,备忘录占用的空间复杂度比较高,使得此时的DP不那么有优势了。
有空看看这个:http://www.hawstein.com/posts/dp-novice-to-advanced.html
递推公式:DP[i][j] = min(DP[i-1][ j], DP[i][j-1]) + grid[i][j] i>0, j>0
DP[i][j-1] + grid[i][j] i=0, j>0
DP[i-1][j] + grid[i][j] i>0, j=0
grid[0][0] otherwise
code:
int minPathSum(vector<vector<int> > &grid) {
int m = grid.size();
if (m == 0) return 0;
int n = grid[0].size();
vector<vector<int>> rs;
for(int i = 0; i<m; ++i) {
vector<int> tmp(n,0);
rs.push_back(tmp);
}
//init left, up
rs[0][0] = grid[0][0];
for (int i=1; i<m; ++i) rs[i][0] += (rs[i-1][0] + grid[i][0]);
for (int i=1; i<n; ++i) rs[0][i] += (rs[0][i-1] + grid[0][i]);
for (int i=1; i<m; ++i) {
for (int j=1; j<n; ++j)
rs[i][j] += (min(rs[i-1][j], rs[i][j-1]) + grid[i][j]); //递推公式
}
return rs[m-1][n-1];
}
上面的空间复杂度为O(m*n), 可以有更space efficient的做法吗?
其实就是和上面的解法一样,但是用len = n的数组去存,因为我们在用矩阵存的时候其实在浪费空间,我们只用到了相邻的item的信息,并未用矩阵其他地方的信息,所以一维就够了
code:
int minPathSum(vector<vector<int> > &grid) {
int m = grid.size();
if (m == 0) return 0;
int n = grid[0].size();
int *row = new int[n];
row[0] = grid[0][0];
for (int i=1; i<n; ++i) row[i] = row[i-1] + grid[0][i];
for (int i=1; i<m; ++i) {
row[0] += grid[i][0];
for (int j=1; j<n; ++j) {
row[j] = min(row[j-1], row[j]) + grid[i][j];
}
}
int rs = row[n-1];
delete [] row;
return rs;
}
2. Unique Paths
和上面的思路一模一样。
int uniquePaths(int m, int n) {
int *rs = new int[n];
for (int i=0; i<n; ++i) rs[i] = 1;
for (int i=1; i<m; ++i) {
for (int j=1; j<n; ++j)
rs[j] += rs[j-1];
}
int ret = rs[n-1];
delete [] rs;
return ret;
}
3. Unique Paths II
和上面思路也一样,注意细节的处理
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) {
vector<vector<int>> dp;
int m = obstacleGrid.size();
if (m == 0) return 0;
int n = obstacleGrid[0].size();
for (int i=0; i<m; ++i) {
vector<int> tmp(n, 1); dp.push_back(tmp);
}
//init
for (int i=0; i<m; ++i) {
for (int j=0; j<n; ++j)
if (obstacleGrid[i][j] == 1) dp[i][j] = 0;
}
if (dp[0][0] == 0) return 0;
//init top: 111110000000000...
for (int i=1; i<n; ++i) dp[0][i] = dp[0][i] == 0 ? 0 : dp[0][i-1];
//calculate
for (int i=1; i<m; ++i) {
dp[i][0] = dp[i][0] == 0 ? 0 : dp[i-1][0]; //init left
for (int j=1; j<n; ++j) dp[i][j] = dp[i][j] == 0 ? 0 : dp[i-1][j] + dp[i][j-1];
}
return dp[m-1][n-1];
}
};