矩阵的最大非负积
难度:
标签:
题目描述
给你一个大小为 m x n
的矩阵 grid
。最初,你位于左上角 (0, 0)
,每一步,你可以在矩阵中 向右 或 向下 移动。
在从左上角 (0, 0)
开始到右下角 (m - 1, n - 1)
结束的所有路径中,找出具有 最大非负积 的路径。路径的积是沿路径访问的单元格中所有整数的乘积。
返回 最大非负积 对 109 + 7
取余 的结果。如果最大积为 负数 ,则返回 -1
。
注意,取余是在得到最大积之后执行的。
示例 1:

输入:grid = [[-1,-2,-3],[-2,-3,-3],[-3,-3,-2]] 输出:-1 解释:从 (0, 0) 到 (2, 2) 的路径中无法得到非负积,所以返回 -1 。
示例 2:

输入:grid = [[1,-2,1],[1,-2,1],[3,-4,1]] 输出:8 解释:最大非负积对应的路径如图所示 (1 * 1 * -2 * -4 * 1 = 8)
示例 3:

输入:grid = [[1,3],[0,-4]] 输出:0 解释:最大非负积对应的路径如图所示 (1 * 0 * -4 = 0)
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 15
-4 <= grid[i][j] <= 4
代码结果
运行时间: 26 ms, 内存: 16.1 MB
/*
* 思路:
* 虽然使用 Java Stream 不能直接处理动态规划的问题,但我们可以通过 Stream API 来简化代码。
* 我们依然维护两个矩阵 dpMax 和 dpMin,然后在每一步中使用 Stream 来更新它们的值。
*/
import java.util.stream.IntStream;
public class Solution {
public int maxProductPath(int[][] grid) {
int MOD = 1000000007;
int m = grid.length, n = grid[0].length;
long[][] dpMax = new long[m][n];
long[][] dpMin = new long[m][n];
dpMax[0][0] = dpMin[0][0] = grid[0][0];
// 初始化第一行
IntStream.range(1, n).forEach(j -> dpMax[0][j] = dpMin[0][j] = dpMax[0][j - 1] * grid[0][j]);
// 初始化第一列
IntStream.range(1, m).forEach(i -> dpMax[i][0] = dpMin[i][0] = dpMax[i - 1][0] * grid[i][0]);
// 填充 dp 数组
IntStream.range(1, m).forEach(i -> {
IntStream.range(1, n).forEach(j -> {
if (grid[i][j] >= 0) {
dpMax[i][j] = Math.max(dpMax[i - 1][j], dpMax[i][j - 1]) * grid[i][j];
dpMin[i][j] = Math.min(dpMin[i - 1][j], dpMin[i][j - 1]) * grid[i][j];
} else {
dpMax[i][j] = Math.min(dpMin[i - 1][j], dpMin[i][j - 1]) * grid[i][j];
dpMin[i][j] = Math.max(dpMax[i - 1][j], dpMax[i][j - 1]) * grid[i][j];
}
});
});
long result = dpMax[m - 1][n - 1];
return result < 0 ? -1 : (int)(result % MOD);
}
}
解释
方法:
该题解采用动态规划的方法来解决问题。定义两个二维数组 max_product 和 min_product,其中 max_product[i][j] 存储到达单元格 (i, j) 的所有路径中可能的最大积,min_product[i][j] 存储可能的最小积。这是因为负数的存在,最小积乘以负数可能变成最大积。初始状态设置为 max_product[0][0] 和 min_product[0][0] 等于 grid[0][0]。然后分别初始化矩阵的第一行和第一列。对于其他单元格,根据当前单元格值的正负来决定如何更新 max_product 和 min_product。遍历完成后,max_product[m-1][n-1] 存储的就是到达右下角的最大积。如果这个最大积小于 0,直接返回 -1;否则,返回其对 10^9+7 取模的结果。
时间复杂度:
O(m*n)
空间复杂度:
O(m*n)
代码细节讲解
🦆
在动态规划中,为什么需要同时维护最大积`max_product`和最小积`min_product`两个矩阵?
▷🦆
该算法如何处理矩阵中的负数值,以确保最终可能得到一个非负的最大积?
▷🦆
在初始化第一行和第一列时,如果遇到0或负数,这对`max_product`和`min_product`的更新有什么特别影响?
▷🦆
为什么在填充每个格子时,需要同时考虑来自上方和左侧的路径?是否有可能只考虑一个方向会导致结果不准确?
▷