[LeetCode] 1289. Minimum Falling Path Sum II 下降路径最小和之二
Given an n x n
integer matrix grid
, return the minimum sum of a falling path with non-zero shifts.
A falling path with non-zero shifts is a choice of exactly one element from each row of grid
such that no two elements chosen in adjacent rows are in the same column.
Example 1:
Input: arr = [[1,2,3],[4,5,6],[7,8,9]]
Output: 13
Explanation:
The possible falling paths are:
[1,5,9], [1,5,7], [1,6,7], [1,6,8],
[2,4,8], [2,4,9], [2,6,7], [2,6,8],
[3,4,8], [3,4,9], [3,5,7], [3,5,9]
The falling path with the smallest sum is [1,5,7], so the answer is 13.
Example 2:
Input: grid = [[7]]
Output: 7
Constraints:
n == grid.length == grid[i].length
1 <= n <= 200
-99 <= grid[i][j] <= 99
这道题是之前那道 Minimum Falling Path Sum 的拓展,在其基础上新加了一个限制条件,就是下降路径中的相邻两行的数字不能在同一列,让求下降路径中的数字之和是多少。这道题其实跟 Paint House II 这道题很类似,那里的房子就相当于这里的行,颜色就这里的列,相邻的房子不能涂相同的颜色就相当于这里相邻的行的数字不能在同一列一样,花费就是这里的数字,基本上可以说是一模一样了。这里还是要用动态规划 Dynamic Programming 来做,用一个二维的 DP 数组,其中 dp[i][j] 表示目前下降到了第i行,且选取了第j列的数字时的下降路径之和,那么最终结果就是 dp[n-1] 中的最小的那个值。接下来想状态转移方程怎么写,既然第i行选择选择了第j列,那么第 i-1 行就一定不能选第j列的(即便第j列的数字可能是最小的),所以第 i-1 行就要选除了第j列以外的数中的最小值,可以额外用一个 for 循环找出来,那么 dp[i][j] 就可以用 grid[i][j] + dp[i-1][k]
来更新了,k遍历第j列以外的所有列,最终在 dp[n-1] 中找到最小值返回即可,参见代码如下:
解法一:
class Solution {
public:
int minFallingPathSum(vector<vector<int>>& grid) {
int n = grid.size(), res = INT_MAX;
vector<vector<int>> dp(n, vector<int>(n));
dp[0] = grid[0];
for (int i = 1; i < n; ++i) {
for (int j = 0; j < n; ++j) {
dp[i][j] = INT_MAX;
for (int k = 0; k < n; ++k) {
if (k != j) dp[i][j] = min(dp[i][j], grid[i][j] + dp[i - 1][k]);
}
}
}
return *min_element(dp[n - 1].begin(), dp[n - 1].end());
}
};
我们可以进一步的优化时间复杂度和空间复杂度,使用一个一维的 DP 数组,其中 dp[i] 表示下降路径的最后一行数字选择了第i列上的数字时的路径之和。这里是一层一层更新,每次会覆盖掉上一层的数据,在更新下一层 dp 之前,要现将当前层的最小值和其列位置,以及第二小值找出来。因为更新下一层的某一个列位置,不能用当前层同一列位置的 dp 值,所以只要和最小值的列位置比较,若不相等则用最小值,若相等,则用第二小值即可,用该 dp 值加上下一层位置的数字就是该位置新的 dp 值了,最后返回的结果是 dp 数组中的最小值,参见代码如下:
解法二:
class Solution {
public:
int minFallingPathSum(vector<vector<int>>& grid) {
int n = grid.size(), res = INT_MAX;
vector<int> dp = grid[0];
for (int i = 0; i < n - 1; ++i) {
int mn = INT_MAX, idx = -1, sm = INT_MAX;
for (int j = 0; j < n; ++j) {
if (dp[j] < mn) {
sm = mn;
mn = dp[j];
idx = j;
} else if (dp[j] < sm) {
sm = dp[j];
}
}
for (int j = 0; j < n; ++j) {
dp[j] = grid[i + 1][j] + (j == idx ? sm : mn);
}
}
return *min_element(dp.begin(), dp.end());
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1289
类似题目:
参考资料:
https://leetcode.com/problems/minimum-falling-path-sum-ii/
https://leetcode.com/problems/minimum-falling-path-sum-ii/discuss/689243/Java-Simple-DP
https://leetcode.com/problems/minimum-falling-path-sum-ii/discuss/452207/C%2B%2B-O(nm)-or-O(1)
https://leetcode.com/problems/minimum-falling-path-sum-ii/discuss/723719/C%2B%2B-easy-DP-O(N*N)