乘积最大子数组

这篇博客介绍了如何使用动态规划解决LeetCode152题——找到数组中乘积最大的子数组。文章通过错误的推导引出正确的方法,分析了正负数对最大乘积的影响,提出了维护最大值和最小值的动态规划状态转移方程,并给出了优化空间。最后展示了两种不同的Java实现代码。
摘要由CSDN通过智能技术生成

 LeetCode152乘积最大子数组

方法一:动态规划

引出:根据经验[LeetCode53, 最大子序和],很容易推导出如下的状态转移方程:

  • f_{max}(i)=max(f_{max}(i-1) \times a_i, a_i)

但这样的推导是错误的?为什么呢?:不满足最优子结构

  • 比如a=[{5, 6, -3, 4, -3}], 对应的f_{max}=[5, 30, -3, 4, -3], 其中的最大值为30, 很明显正确的答案应该是5 \times 6 \times -3 \times 4 \times -3 = 30 \times 36 = 1080

如何 解决这个问题呢?从正负性角度出发来分析

  • 如果当前值a_i为正数,期望以它前一个位置结尾的某个段的积也是个正数,而且尽可能大。
  • 如果当前值a_i为负数,期望以它前一个位置结尾的某个段的积也是个负数,而且尽可能小(负数的绝对值越大)。
  • 因此我们可以再维护一个f_{min}(i),表示以第i个元素为结尾的最小子数组的乘积,可以得到如下完整的动态规划转移方程,分别为取三个值的最值。

\\f_{max}(i) = Max(f_{max}(i-1)\times a_i, f_{min}(i-1)\times a_i, a_i)\\ f_{min}(i) = Min(f_{max}(i-1)\times a_i, f_{min}(i-1)\times a_i, a_i)

实现代码

class Solution {
    public int maxProduct(int[] nums) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        int length = nums.length;
        int[] fmax = new int[length];
        fmax[0] = nums[0];
        int[] fmin = new int[length];
        fmin[0] = nums[0];
        int ans = fmax[0];

        for(int i = 1; i <= length - 1; i++){
            fmax[i] = Math.max(fmax[i-1] * nums[i], Math.max(fmin[i-1] * nums[i], nums[i]));
            fmin[i] = Math.min(fmax[i-1] * nums[i], Math.min(fmin[i-1] * nums[i], nums[i]));
            ans = Math.max(ans, fmax[i]);
        }
        return ans;
    }
}

优化空间

i个状态之和第i-1个状态相关,我们可以只用两个变量来维护i-1时刻的状态,一个维护f_{max},一个维护f_{min}

实现代码

public class Solution {
    public double maxProduct(double[] arr) {
        double res = Double.MIN_VALUE;
        if(arr == null || arr.length == 0){
            return res;
        }
        int len = arr.length;
        double fmax = arr[0];
        double fmin = arr[0];
        res = fmax;
        for(int i = 1; i < len; i++){
            double newFmax = Math.max(Math.max(fmax * arr[i], fmin * arr[i]), arr[i]);
            double newFmin = Math.min(Math.min(fmax * arr[i], fmin * arr[i]), arr[i]);
            fmax = newFmax;
            fmin = newFmin;
            res = Math.max(res, fmax);
        }
        return res;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值