【dp】最大子数组和&乘积最大子数组

文章目录

  • 53. 最大子数组和
  • 152. 乘积最大子数组

53. 最大子数组和

【dp】最大子数组和&乘积最大子数组_第1张图片
dp[i]:用到了nums[i]连续子数组的最大和

用到了nums[i]连续子数组的最大和dp[i],要么是前面用到了nums[i - 1]的连续子数组最大和dp[i - 1],要么就是nums[i]

class Solution {
public:
    // dp[i]:用到了nums[i]连续子数组的最大和
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n, 0);
        dp[0] = nums[0];
        int ans = nums[0];
        for(int i = 1; i < n; i++){
            dp[i] = max(dp[i - 1] + nums[i], nums[i]);
            ans = max(ans, dp[i]);
        }
        return ans;
    }
};

152. 乘积最大子数组

【dp】最大子数组和&乘积最大子数组_第2张图片

数组的动态规划问题、连续子序列的一个常见的状态定义是:以下标 i 结尾的连续子序列的乘积的最大值

最后把整个 dp 数组看一遍求最大值即可。因此状态转移方程可能是:dp[i] = max(dp[i - 1] * nums[i], nums[i])

说明:牢记状态的定义,一定以下标 i 结尾,即:乘积数组中 nums[i] 必须被选取

针对这道题,dp状态有两维,第二维的状态有两个:

  • dp[i][0]:用 0 表示遍历的过程中得到的以 nums[i] 结尾的连续子序列的乘积的最小值
  • dp[i][1]:用 1 表示遍历的过程中得到的以 nums[i] 结尾的连续子序列的乘积的最大值
  1. nums[i] >= 0

    • 以nums[i]结尾的连续子序列的最大值dp[i][1],要么是前面序列最大乘积dp[i-1][1]用到了nums[i]的乘积,要么是放弃前面的序列dp[i-1][1],最大乘积就是nums[i]
    • 以nums[i]结尾的连续子序列的最小值dp[i][0],要么是前面序列最小乘积dp[i-1][0]用到了nums[i]的乘积,要么是放弃前面的序列dp[i-1][0],最小乘积就是nums[i]
  2. nums[i] < 0

    • 以nums[i]结尾的连续子序列的最大值dp[i][1],要么是前面序列最小乘积dp[i-1][0]用到了nums[i]的乘积,要么是放弃前面的序列dp[i-1][0],最大乘积就是nums[i]
    • 以nums[i]结尾的连续子序列的最小值dp[i][0],要么是前面序列最大乘积dp[i-1][1]用到了nums[i]的乘积,要么是放弃前面的序列dp[i-1][1],最小乘积就是nums[i]

动态规划(理解无后效性)

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        // dp[i][0]:用到nums[i]的最小乘积
        // dp[i][1]:用到nums[i]的最大乘积
        vector<vector<int>> dp(n, vector<int>(2, 0));
        dp[0][0] = nums[0];
        dp[0][1] = nums[0];
        int ans = dp[0][1];
        for(int i = 1; i < n; i++){
            if(nums[i] >= 0){
                dp[i][1] = max(nums[i], dp[i - 1][1] * nums[i]);
                dp[i][0] = min(nums[i], dp[i - 1][0] * nums[i]);
            }else{
                dp[i][1] = max(nums[i], dp[i - 1][0] * nums[i]);
                dp[i][0] = min(nums[i], dp[i - 1][1] * nums[i]);
            }
            ans = max(ans, dp[i][1]);
        }
        return ans;
    }
};

思路: 求最大值,可以看成求被0拆分的各个子数组的最大值

当一个数组中没有0存在,则分为两种情况:

  1. 负数为偶数个,则整个数组的各个值相乘为最大值;

  2. 负数为奇数个,则从左边开始,乘到最后一个负数停止有一个“最大值”,从右边也有一个“最大值”,比较,得出最大值

正反向各遍历一次,当前数nums[i]为0时,则把当前乘积mul重新置为1,ans记录遍历过程中最大的mul

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        int mul = 1;
        int ans = nums[0];       
        for(int i = 0; i < n; i++){
            mul *= nums[i];      // 记录[0,i]区间内的乘积
            if(mul > ans) ans = mul;
            if(nums[i] == 0) mul = 1;  // 若当前值为0,则mul重新置为1
        }
        mul = 1;
        for(int i = n - 1; i >= 0; i--){
            mul *= nums[i];
            if(mul > ans) ans = mul;
            if(nums[i] == 0) mul = 1;
        }
        return ans;
    }
};

你可能感兴趣的:(剑指offer,动态规划,leetcode,算法)