base case 和备忘录的初始值怎么定? :: labuladong的算法小抄 #997
Replies: 47 comments 14 replies
-
东哥,我有一个问题不是很明白,之前框架的思路是:明确 base case -> 明确「状态」-> 明确「选择」 -> 定义 dp 数组/函数的含义。但是这个题却说得是:base case是根据 dp 函数的定义所决定的。这个该怎么理解呢? |
Beta Was this translation helpful? Give feedback.
-
memo[i][j] = matrix[i][j] + min( |
Beta Was this translation helpful? Give feedback.
-
所以代码中使用了备忘录memo[][]数组来避免,如果matrix[i][j]计算过,就已经把结果存入到memo[i][j]。 if (memo[i][j] != 66666) {
return memo[i][j];
} |
Beta Was this translation helpful? Give feedback.
-
这样写感觉比较好理解,跟题目逻辑保持一致 /**
* 状态转移方程,对于nums[row][col]这个位置,其最小下降路径和为:
* dp[row][col] = nums[row][col] + Math.min(nums[row+1][col-1], nums[row+1][col], nums[row+1][col+1])
* base case为落到底时,注意检查col是否越界
* TC=O(N^2),SC=O(N^2)
*/
public int minFallingPathSum(int[][] matrix) {
int n = matrix.length;
dp = new int[n+1][n+1];
for(int[] d : dp){
// 初始化dp数组
Arrays.fill(d, Integer.MAX_VALUE);
}
int min = Integer.MAX_VALUE;
for(int i = 0; i < n; i++){
// 逐个计算第一行的每个元素的最小下降路径和,取最小的一个
min = Math.min(min, matrix(matrix, 0, i));
}
return min;
}
int[][] dp;
int matrix(int[][] matrix, int row, int col){
int n = matrix.length;
// base case
// 列越界
if(col < 0 || col >= n) return Integer.MAX_VALUE;
// 落到底
if(row == n - 1) return matrix[row][col];
// 查询备忘录
if(dp[row][col] != Integer.MAX_VALUE) return dp[row][col];
int res = matrix[row][col] + Math.min(Math.min(matrix(matrix, row+1, col-1), matrix(matrix, row+1, col)),
matrix(matrix, row+1, col+1));
// 保存结果到备忘录
dp[row][col] = res;
return res;
} |
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
这道题和之前之后的的文章有点不一样,之前都是dp[]或dp[][],为什么这里要用一个函数呢?为什么不用二维dp数组呢?什么时候该用函数呢? |
Beta Was this translation helpful? Give feedback.
-
贴一个很暴力的动态规划
class Solution {
public int minFallingPathSum(int[][] matrix) {
int[][] dp = new int[matrix.length][matrix[0].length];
int min = Integer.MAX_VALUE;
if(matrix.length == 1){
return matrix[0][0];
}
for(int i = 0; i<matrix.length ; i++){
for(int j = 0; j<matrix[0].length; j++){
if(i == 0){
dp[i][j] = matrix[i][j];
}else if (j == 0){
dp[i][j] = matrix[i][j] + Math.min(dp[i-1][j],dp[i-1][j+1]);
}else if(j == matrix.length-1){
dp[i][j] = matrix[i][j] + Math.min(dp[i-1][j],dp[i-1][j-1]);
}else{
dp[i][j] = Math.min(Math.min(dp[i-1][j+1],dp[i-1][j]),dp[i-1][j-1])+matrix[i][j];
}
if(i == matrix.length-1){
min = Math.min(min,dp[i][j]);
}
}
}
return min;
}
} |
Beta Was this translation helpful? Give feedback.
-
补一个dp数组解法: function minFallingPathSum(matrix: number[][]): number {
let boundry = matrix.length // 方形数组边界
// 辅助函数
const min = (...args:number[]):number => {
let vals = args.filter(Boolean)
let res = vals[0]
for(let i = 1; i < vals.length; i++){
if(vals[i]<res)res = vals[i]
}
return res
}
let res = Number.MAX_SAFE_INTEGER
let dp = new Array(boundry)
for(let i = 0; i < dp.length; i++){
dp[i] = new Array(boundry).fill(0)
}
for(let i = 0; i < boundry; i++){
for(let j = 0; j < boundry; j++){
if(i===0){
dp[i][j] = matrix[i][j]
}else{
if(j-1<0){
dp[i][j] = matrix[i][j] + min(dp[i-1][j],dp[i-1][j+1])
}else if(j+1>boundry){
dp[i][j] = matrix[i][j] + min(dp[i-1][j-1],dp[i-1][j])
}else{
dp[i][j] = matrix[i][j] + min(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1])
}
}
}
}
return min(...dp[boundry-1])
}; |
Beta Was this translation helpful? Give feedback.
-
贴个dp数组解法,重新发一下,刚刚那个代码高亮😂 function minFallingPathSum(matrix: number[][]): number {
if(matrix.length === 0) {
return 0
}
const MAX = 100 * 100 + 1
const m = matrix.length
const n = matrix[0].length
const dp = new Array(m + 1)
dp[0] = new Array(n + 2).fill(0)
for(let i = 1; i < dp.length; i++) {
if(dp[i] === undefined) {
dp[i] = new Array(n + 2).fill(MAX)
}
for(let j = 1; j <= n; j++) {
dp[i][j] = Math.min(dp[i - 1][j - 1], dp[i - 1][j], dp[i - 1][j + 1]) + matrix[i - 1][j - 1]
}
}
return Math.min(...dp[dp.length - 1])
}; |
Beta Was this translation helpful? Give feedback.
-
#include <vector>
#define MAX_LIMIT 10000
using namespace std;
int minFallingPathSum(vector<vector<int>> &matrix) {
const auto m = matrix.size(), n = matrix[0].size();
vector<vector<int>> dp(m, vector<int>(n));
for (auto i = 0; i < m; i++) {
for (auto j = 0; j < n; j++) {
if (!i) {
dp[i][j] = matrix[i][j];
continue;
}
dp[i][j] = dp[i - 1][j] + matrix[i][j];
if (j - 1 >= 0)
dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + matrix[i][j]);
if (j + 1 < n)
dp[i][j] = min(dp[i][j], dp[i - 1][j + 1] + matrix[i][j]);
}
}
int res = MAX_LIMIT;
for (auto x: dp[m - 1])
res = min(res, x);
return res;
} |
Beta Was this translation helpful? Give feedback.
-
c++超时,思路就是一模一样的思路,代码如下: class Solution {
public:
vector<vector<int>> memo;//备忘录
int minFallingPathSum(vector<vector<int>> matrix) {
int n = matrix.size();
int res = INT_MAX;
//备忘录初试化为特殊值666666
memo.resize(n);
fill(memo.begin(), memo.end(), vector<int>(n, 666666));
for (int j = 0; j < n; j++) {
// 终点可能在 matrix[n-1] 的任意一列
res = min(res, dp(matrix, n - 1, j));
}
return res;
}
inline int dp(vector<vector<int>> matrix, int i, int j) {
int n = matrix.size();
//非法索引检查
if (i < 0 || j < 0 ||
i >= n || //注意这里是大于等于
j >= matrix[0].size())
{
return 999999;//返回一个特殊值
}
//base case
if (i == 0) {
return matrix[i][j];
}
//查备忘录。防止重复计算
if (memo[i][j] != 666666) {
return memo[i][j];
}
//状态转移,并且将计算的结果存入备忘录
memo[i][j] = matrix[i][j] + min(dp(matrix, i - 1, j),
min(dp(matrix, i - 1, j - 1), dp(matrix, i - 1, j + 1)));
return memo[i][j];
}
}; |
Beta Was this translation helpful? Give feedback.
-
@q240627995 C++ 里面递归函数的数组参数要传引用 |
Beta Was this translation helpful? Give feedback.
-
完美解决!感谢!!!学以致用,理论结合实际真的很重要!面经背的很熟:引用减少开销、可以修改数组内部值巴拉巴拉,实际coding时却常常忘记引用传入! |
Beta Was this translation helpful? Give feedback.
-
贴一个好理解的数组解法 class Solution {
public int minFallingPathSum(int[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
int[][] dp = new int[m][n];
// base case
for(int i = 0; i < n; i++){
dp[0][i] = matrix[0][i];
}
for(int i = 1; i < m; i++){
for(int j = 0; j < n; j++){
if(j < 1){
dp[i][j] = matrix[i][j] + Math.min(dp[i - 1][j], dp[i - 1][j + 1]);
}
else if(j >= n - 1){
dp[i][j] = matrix[i][j] + Math.min(dp[i - 1][j], dp[i - 1][j - 1]);
}
else{
dp[i][j] = matrix[i][j] + getMin(dp[i - 1][j], dp[i - 1][j - 1], dp[i - 1][j + 1]);
}
}
}
int min = Integer.MAX_VALUE;
for(int i = 0; i < n; i++){
min = Math.min(min, dp[m - 1][i]);
}
return min;
}
public int getMin(int a, int b, int c){
return Math.min(a, Math.min(b, c));
}
} |
Beta Was this translation helpful? Give feedback.
-
@xiaoxu-git 前面说错了,是先定义dp,然后推导方程式,然后才知道base case咋定义。 |
Beta Was this translation helpful? Give feedback.
-
贴一个省空间的解法 var minFallingPathSum = function(matrix) {
const length = matrix.length;
if (length == 1) return matrix[0][0];
let res = Infinity;
for (let i = 1; i < length; i ++) {
for (let j = 0; j < length; j ++) {
if (j == 0) {
matrix[i][j] = matrix[i][j] + Math.min(matrix[i - 1][j], matrix[i - 1][j + 1]);
}else if (j == length - 1) {
matrix[i][j] = matrix[i][j] + Math.min(matrix[i - 1][j], matrix[i - 1][j - 1]);
}else {
matrix[i][j] = matrix[i][j] + Math.min(matrix[i - 1][j], matrix[i - 1][j - 1], matrix[i - 1][j + 1]);
}
if (i == length - 1) res = Math.min(res, matrix[i][j]);
}
}
return res;
}; |
Beta Was this translation helpful? Give feedback.
-
Python 选手: 自低向上 class Solution:
def minFallingPathSum(self, matrix: List[List[int]]) -> int:
n = len(matrix)
row = [None for _ in range(n)]
dp = [row.copy() for _ in range(n)]
# definition
#dp[i][j]: the distance from i,j to top
# base case
dp[0] = matrix[0]
for row in range(1,n):
for col in range(n):
# (row + 1, col - 1)
left_val = float('inf')
if 0<=row - 1<n and 0<=col + 1<n:
left_val = dp[row - 1][col + 1]
# (row + 1, col)
right_val = float('inf')
if 0<=row-1<n:
down_val = dp[row - 1][col]
# (row + 1, col + 1)
right_val = float('inf')
if 0<=row - 1<n and 0<=col - 1<n:
right_val = dp[row - 1][col - 1]
dp[row][col] = min([left_val,down_val,right_val])+ + matrix[row][col]
return min(dp[-1]) |
Beta Was this translation helpful? Give feedback.
-
从上到下找遍历每一个数字,找这个数字分别加上一行三个数字的最小值,存在这个数字的位置,直到最后一行返回最后一行的最小数字 class Solution {
public int minFallingPathSum(int[][] matrix) {
if (matrix==null || matrix.length==0) return 0;
int m = matrix.length; int n = matrix[0].length;
int[][] sum = matrix.clone();
int res = Integer.MAX_VALUE;
for (int i=0; i<m; i++){
for (int j=0; j<n; j++){
if (i!=0){
int min = Integer.MAX_VALUE;
for (int k=j-1; k<=j+1; k++){
if (k<0 || k==n) continue;
min = Math.min(matrix[i-1][k]+sum[i][j], min);
}
sum[i][j] = min;
}
if (i==m-1) res = Math.min(res, sum[i][j]);
}
}
return res;
}
} |
Beta Was this translation helpful? Give feedback.
-
贴一个python class Solution:
def minFallingPathSum(self, matrix: List[List[int]]) -> int:
@cache
def dp(i, j):
# 检查索引
nonlocal n
if i < 0 or j < 0 or i >= n or j >= n:
return 99999
# base case
if i == 0:
return matrix[0][j]
return matrix[i][j] + min(dp(i-1, j), dp(i-1, j-1), dp(i-1, j+1))
n = len(matrix)
res = inf
for j in range(n):
res = min(res, dp(n-1, j))
return res |
Beta Was this translation helpful? Give feedback.
-
感觉这样写更容易理解,从上往下递归 class Solution {
int[][] memo;
public int minFallingPathSum(int[][] matrix) {
int w = matrix[0].length;
memo = new int[matrix.length][w];
for (int i = 0; i < w; i++) {
Arrays.fill(memo[i], 66666);
}
int res = Integer.MAX_VALUE;
for(int i = 0; i < w;i++){
res = Math.min(res,dp(matrix,0,i));
}
return res;
}
public int dp(int[][] matrix, int i, int j){
if(i > matrix.length - 1) return 0;
int a = Integer.MAX_VALUE,b = a,c = a;
i++;
if(memo[i - 1][j] != 66666){
return memo[i - 1][j];
}
if(j == 0){
c = dp(matrix,i,j + 1);
}else if(j == matrix[0].length - 1){
a = dp(matrix,i,j - 1);
}else{
a = dp(matrix,i,j - 1);
c = dp(matrix,i,j + 1);
}
b = dp(matrix,i,j);
memo[i - 1][j] = matrix[i - 1][j] + Math.min(a,Math.min(b,c));
return memo[i - 1][j];
}
} |
Beta Was this translation helpful? Give feedback.
-
自底向上(填满memo的每一项)可能容易理解点呢。 def minFallingPathSum(self, matrix: List[List[int]]) -> int:
row, col = len(matrix), len(matrix[0])
self.memo = [[102*100]*col for i in range(row)]
self.memo[0] = matrix[0]
for i in range(1, row):
for j in range(col):
cut = slice(max(0, j-1), min(j+2, col))
temp = self.memo[i-1][cut]
self.memo[i][j] = min(self.memo[i][j], min(temp)+matrix[i][j])
# print(self.memo)
return min(self.memo[-1]) |
Beta Was this translation helpful? Give feedback.
-
为什么这样写的时间复杂度很高,用的自顶向上的dp方法(难道是因为有两个循环?) |
Beta Was this translation helpful? Give feedback.
-
改写了一个小学生写法的 非递归版dp写法 class Solution {
} |
Beta Was this translation helpful? Give feedback.
-
补一个使用额外空间的方法,代码会看起来简单一些
|
Beta Was this translation helpful? Give feedback.
-
使用dp数组为什么会比递归慢这么多呀???一个1ms,一个5ms。按理说都是加了备忘录的 |
Beta Was this translation helpful? Give feedback.
-
有几个疑问: 1.我这里把二维的dp数组降为一维的dp数组,但是在commit的时候,空间复杂度按道理来说应该是O(N),但是结果却比二维数组的效果差,这是不能理解的 class Solution {
public int minFallingPathSum(int[][] matrix) {
int n = matrix.length;
int[] dp = new int[n + 2];
dp[0] = dp[n + 1] = Integer.MAX_VALUE;
int min = Integer.MAX_VALUE;
for (int i = 0; i < n; i++) {
int preVal = dp[0];
for (int j = 0; j < n; j++) {
//base case
if (i == 0) {
dp[j + 1] = matrix[i][j];
} else {
int tempVal = dp[j + 1];
dp[j + 1] = matrix[i][j] + Math.min(Math.min(preVal, dp[j + 1]), dp[j + 2]);
preVal = tempVal;
}
//n==last index,统计结果
if (i == n - 1) {
min = Math.min(min, dp[j + 1]);
}
}
}
return min;
}
} |
Beta Was this translation helpful? Give feedback.
-
66666 99999 这些用 math.MaxInt32 代替就可以了吧?(golang) |
Beta Was this translation helpful? Give feedback.
-
文章链接点这里:base case 和备忘录的初始值怎么定?
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions