zoukankan      html  css  js  c++  java
  • 152乘积最大子数组

    题目:给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

    转自:https://blog.csdn.net/lw_power/article/details/106185074

    动态规划问题
    「动态规划」通常不关心过程,只关心「阶段结果」,这个「阶段结果」就是我们设计的「状态」。什么算法关心过程呢?「回溯算法」,「回溯算法」需要记录过程,复杂度通常较高。

    第 1 步:状态设计

    dp[i][j]:以 nums[i] 结尾的连续子数组的最值,计算最大值还是最小值由 j 来表示,j 就两个值;
    当 j = 0 的时候,表示计算的是最小值;
    当 j = 1 的时候,表示计算的是最大值。
    这样一来,状态转移方程就容易写出。

    第 2 步:推导状态转移方程

    由于状态的设计 nums[i] 必须被选取(这一点使得子数组、子序列问题更加简单:当情况复杂、分类讨论比较多的时候,需要固定一些量,以简化计算);

    nums[i] 的正负和之前的状态值(正负)就产生了联系,由此关系写出状态转移方程:

    1.当 nums[i] > 0 时,由于是乘积关系:

    • 最大值乘以正数依然是最大值;
    • 最小值乘以同一个正数依然是最小值;

    2.当 nums[i] < 0 时,依然是由于乘积关系:

    • 最大值乘以负数变成了最小值;
    • 最小值乘以同一个负数变成最大值;

    3.当 nums[i] = 0 的时候,由于 nums[i] 必须被选取,最大值和最小值都变成 0 ,合并到上面任意一种情况均成立。
    但是,还要注意一点,之前状态值的正负也要考虑:例如,在考虑最大值的时候,当 nums[i] > 0 是,如果 dp[i - 1][1] < 0 (之前的状态最大值) ,此时 nums[i] 可以另起炉灶(这里是第 53 题的思想),此时 dp[i][1] = nums[i] ,合起来写就是:

    dp[i][1] = max(nums[i], nums[i] * dp[i - 1][1]) if nums[i] >= 0

    其它三种情况可以类似写出,状态转移方程如下:

    dp[i][0] = min(nums[i], nums[i] * dp[i - 1][0]) if nums[i] >= 0
    dp[i][1] = max(nums[i], nums[i] * dp[i - 1][1]) if nums[i] >= 0

    dp[i][0] = min(nums[i], nums[i] * dp[i - 1][1]) if nums[i] < 0
    dp[i][1] = max(nums[i], nums[i] * dp[i - 1][0]) if nums[i] < 0

    第 3 步:考虑初始化

    由于 nums[i] 必须被选取,那么 dp[i][0] = nums[0],dp[i][1] = nums[0]。

    第 4 步:考虑输出

    题目问连续子数组的乘积最大值,这些值需要遍历 dp[i][1] 获得。

    //Java
    public class Solution {
    
        public int maxProduct(int[] nums) {
            int len = nums.length;
            if (len == 0) {
                return 0;
            }
    
            // dp[i][0]:以 nums[i] 结尾的连续子数组的最小值
            // dp[i][1]:以 nums[i] 结尾的连续子数组的最大值
            int[][] dp = new int[len][2];
            dp[0][0] = nums[0];
            dp[0][1] = nums[0];
            for (int i = 1; i < len; i++) {
                if (nums[i] >= 0) {
                    dp[i][0] = Math.min(nums[i], nums[i] * dp[i - 1][0]);
                    dp[i][1] = Math.max(nums[i], nums[i] * dp[i - 1][1]);
                } else {
                    dp[i][0] = Math.min(nums[i], nums[i] * dp[i - 1][1]);
                    dp[i][1] = Math.max(nums[i], nums[i] * dp[i - 1][0]);
                }
            }
    
            // 只关心最大值,需要遍历
            int res = dp[0][1];
            for (int i = 1; i < len; i++) {
                res = Math.max(res, dp[i][1]);
            }
            return res;
        }
    }
    

    Time:O(N),这里 N 是数组的长度,遍历 2 次数组;
    Space:O(N),状态数组的长度为 2N。

    1.动态规划
    2. 53 题:最大子序和
    3.状态设计 300 题:最长上升子序列

  • 相关阅读:
    11月20日
    11月19日
    11月26日
    11月25日
    生活有感(一)
    c# word 删除指定内容
    mysql insert语句
    c# 删除word文档中某一页
    mysql 相同表结构拷贝数据
    调试再次出错
  • 原文地址:https://www.cnblogs.com/centralpark/p/12911599.html
Copyright © 2011-2022 走看看