LeetCode 1594.矩阵中最大的非负乘积(动态规划 + 滚动数组)
题目描述
给你一个大小为 m x n 的整数矩阵 grid。从 (0,0) 出发,每次只能向右或向下移动,到达 (m-1, n-1)。路径的乘积定义为路径上所有数字的乘积。请你返回路径乘积的最大值 。如果最大值为负数,则返回 -1。由于答案可能很大,需要对 10^9 + 7 取模。
算法思路
这道题与常规的"最大路径和"不同,因为乘法运算中负数乘负数会变成正数,所以仅仅记录最大值是不够的。我们需要同时记录最大值 和最小值,以便在遇到负数时能够通过"负负得正"得到更大的结果。
动态规划定义
我们采用滚动数组优化空间,只维护当前行的最小值和最大值:
f_min[j]:表示从起点到达当前行第j列的所有路径中,乘积的最小值。f_max[j]:表示从起点到达当前行第j列的所有路径中,乘积的最大值。
遍历每个格子 (i, j),对于当前位置的值 x,其可能的前驱位置只有上方 (i-1, j) 和左方 (i, j-1)。我们利用这两个位置已经计算好的 min 和 max,来更新当前格子的 min 和 max。
状态转移
设 mn 和 mx 分别为前驱格子的最小值和最大值,则到达当前格子后,可能的新乘积为 mn * x 和 mx * x。因此:
- 当前格子的最小值 =
min(所有前驱产生的 mn*x, mx*x) - 当前格子的最大值 =
max(所有前驱产生的 mn*x, mx*x)
由于每一轮我们都只依赖上一行的数据和当前行已计算好的左边数据,因此可以用一维数组滚动更新。
边界条件
- 起点
(0, 0)的f_min[0] = f_max[0] = grid[0][0]。 - 其他格子若无法从上或左到达(即
i == 0或j == 0),则只考虑可行的方向。
代码实现(C++)
cpp
class Solution {
public:
int maxProductPath(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
vector<long long> f_min(n), f_max(n); // 滚动数组,存储当前行的最小/最大乘积
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
long long x = grid[i][j];
if (i == 0 && j == 0) {
f_min[0] = f_max[0] = x;
continue;
}
long long res_min = LLONG_MAX;
long long res_max = LLONG_MIN;
// 从上方转移
if (i > 0) {
long long mn = f_min[j], mx = f_max[j];
res_min = min(mn * x, mx * x);
res_max = max(mn * x, mx * x);
}
// 从左方转移
if (j > 0) {
long long mn = f_min[j - 1], mx = f_max[j - 1];
res_min = min(res_min, min(mn * x, mx * x));
res_max = max(res_max, max(mn * x, mx * x));
}
f_min[j] = res_min;
f_max[j] = res_max;
}
}
long long ans = f_max[n - 1], MOD = 1e9 + 7;
return ans < 0 ? -1 : ans % MOD;
}
};
复杂度分析
- 时间复杂度:O(m × n),遍历整个矩阵一次。
- 空间复杂度:O(n),使用两个长度为 n 的数组滚动更新。
关键点总结
- 负数乘法:必须同时维护最小值和最大值,因为最小值(负数)乘负数可能变为最大值。
- 滚动数组:只用一维数组记录当前行的结果,避免二维数组占用过多空间。
- 取模与负数判断 :最终结果若为负数返回 -1,否则对
10^9+7取模。
示例验证
以 grid = [[1, -2], [3, 4]] 为例:
- 从
(0,0)开始,f_min[0] = f_max[0] = 1。 - 处理
(0,1),只能从左边来:mn=1, mx=1, x=-2,乘积为-2,所以f_min[1] = -2, f_max[1] = -2。 - 处理
(1,0),只能从上边来:mn=1, mx=1, x=3,乘积为3,所以f_min[0] = 3, f_max[0] = 3。 - 处理
(1,1),可从上方(0,1)和左方(1,0)转移:- 从上:
mn=-2, mx=-2, x=4,乘积为-8。 - 从左:
mn=3, mx=3, x=4,乘积为12。 - 最小值
min(-8,12) = -8,最大值max(-8,12) = 12。
- 从上:
- 最终答案
ans = 12,返回12 % MOD = 12。