LeetCode 1594.矩阵中最大的非负乘积

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. 负数乘法:必须同时维护最小值和最大值,因为最小值(负数)乘负数可能变为最大值。
  2. 滚动数组:只用一维数组记录当前行的结果,避免二维数组占用过多空间。
  3. 取模与负数判断 :最终结果若为负数返回 -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。
相关推荐
zyl76510 小时前
FlashAttention 详解:从 GPU 硬件、普通 Attention 到分块计算与 Online Softmax
算法
爱读书的小胖14 小时前
LeetCode 122:买卖股票的最佳时机 II(C)解题分析
算法
结局无敌14 小时前
React Native for OpenHarmony 实战:三方库 react-native-modal-dialog 的鸿蒙化适配指南
深度学习·算法·react native·react.js·harmonyos
OPEN-F14 小时前
ROS2系列教程:上位机建图与导航(原理综合)
算法·机器人
weixin_3077791314 小时前
C++代码实现MATLAB中的fitrtree函数功能
开发语言·c++·算法·matlab
tomwang032215 小时前
路径优化建模、优化算法分类及工程化小结
算法·数学建模·分类
小羊没烦恼!15 小时前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp
2501_9269783315 小时前
人机通信协议--人和机器之间的交互想象
人工智能·经验分享·算法·ai写作
行者全栈架构师15 小时前
【鸿蒙心迹】互动卡片全链路——把核心功能放到桌面(FormKit 实战)(HarmonyOS 7.x)
前端·算法·架构
无定义_15 小时前
10.11日常训练
算法