leetcode 152: 最大乘积数组(动态规划)

题解

  • 题意:给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
  • 题解:此题要求找到连续乘积最大的子数组,连续数组的意义是什么,一个最直观的想法就是,如果前面的乘积在乘了元素i之后变大了,那么就把元素i加入到子数组中,否则,则从第i项开始,再进行记录。
    • 但是这会遇到一个问题,正数如果乘以负数,那么就是变小了,此时如果抛弃掉前面的项,很明显是不对的,因为后面如果又遇到一个负数,那么最小值又变成最大值了,因此我们不能因为乘以了一个负数,就单纯抛弃前面的项。
    • 此时问题似乎变复杂了,但是最大乘积只可能是最小值乘以负数,或者最大值乘以正数得到,因此我们维护一个最小值序列与最大值序列即可,这个状态转移就简单了,就是到第i个项的时候,最大值是从前i-1项的最大乘积得到,还是最小乘积得到,当然,也可能是当前元素,比如前面乘积最大值是负数,突然来了一个正数,那此时最大乘积数值就是它。
    • m a x [ i ] = M A X ( m a x [ i − 1 ] ∗ n u m s [ i ] , M A X ( n u m s [ i ] , m i n [ i − 1 ] ∗ n u m s [ i ] ) ) max[i]=MAX(max[i-1]*nums[i], MAX(nums[i], min[i-1]*nums[i])) max[i]=MAX(max[i1]nums[i],MAX(nums[i],min[i1]nums[i]))
    • m i n [ i ] = M I N ( m i n [ i − 1 ] ∗ n u m s [ i ] , M I N ( n u m s [ i ] , m a x [ i − 1 ] ∗ n u m s [ i ] ) ) min[i]=MIN(min[i-1]*nums[i], MIN(nums[i], max[i-1]*nums[i])) min[i]=MIN(min[i1]nums[i],MIN(nums[i],max[i1]nums[i]))
  • 实现: ,
class Solution {
public int maxProduct(int[] nums) {
    	int n = nums.length;
        int []max = new int[n];
        int []min = new int[n];
        int maxv = nums[0];
        max[0] = nums[0];
        min[0] = nums[0];
        
        for (int i = 1; i < n; ++i) {
            max[i] = Math.max(max[i - 1] * nums[i], Math.max(nums[i], min[i - 1] * nums[i]));
            min[i] = Math.min(min[i - 1] * nums[i], Math.min(nums[i], max[i - 1] * nums[i]));
        	maxv = Math.max(maxv, Math.max(min[i], max[i]));
        }
        return maxv;
    }
};
  • 我们可以发现,其实不需要开两个数组来存储maxmin,因为这两个数字可以相互转换,当遇到负数的时候,最大正数乘积可能会被转换成最小负数乘积,而最小负数乘积就可能会变成最大正数乘积, 而最大乘积也只可能在这两种情况下得到。
    • 举个例子,对于[1,2,3,4,-1,-2]来说,最小乘积数组是[1,2,3,4,-1],最大则是[1,2,3,4,-1,-2],是由最小乘积得到的,但是由最小乘积转换来的正数不一定是最大乘积,比如[1,2,3,4,-1,-2, -1, 1,2,3,4,5],是由[-2,-1,1,2,3,4,5]数组得到的。
    • 那么这样一来,我们还是需要额外利用一个max来记录过程中的最大值,因为最大乘积只可能从最小乘积的下一个状态和最大乘积中获取
  • 实现
class Solution {
    public int maxProduct(int[] nums) {
        int max = nums[0];
        int imin = nums[0], imax = nums[0];
        int n = nums.length;
        for(int i = 1;i < n;i++){
            if(nums[i] < 0) {
                imin = imax^imin; 
                imax ^= imin; 
                imin ^= imax;
            }
            imax = Math.max(nums[i], imax*nums[i]);
            imin = Math.min(nums[i], imin*nums[i]);
            max = Math.max(max, imax);
        }
        return max;
    }
}

题目

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值