【LeetCode 刷题】动态规划之“使用滚动数组的优化”

动态规划之“滚动数组优化”

概要

滚动数组是一种能够在动态规划中降低空间复杂度的方法,有时某些二维dp方程可以直接降阶到一维,在某些题目中甚至可以降低时间复杂度,是一种极为巧妙的思想。
简要来说就是通过观察dp方程来判断需要使用哪些数据,可以抛弃哪些数据,一旦找到关系,就可以用新的数据不断覆盖旧的数据量来减少空间的使用。

LeetCode1567. 乘积为正数的最长子数组长度

题目描述

给你一个整数数组 nums ,请你求出乘积为正数的最长子数组的长度。

一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。

请你返回乘积为正数的最长子数组长度。

示例 1:

输入:nums = [1,-2,-3,4]
输出:4
解释:数组本身乘积就是正数,值为 24

示例 2:

输入:nums = [0,1,-2,-3,-4]
输出:3
解释:最长乘积为正数的子数组为 [1,-2,-3] ,乘积为 6 。
注意,我们不能把 0 也包括到子数组中,因为这样乘积为 0 ,不是正数。

示例 3:

输入:nums = [-1,-2,-3,0,1]
输出:2
解释:乘积为正数的最长子数组是 [-1,-2] 或者 [-2,-3]

提示:

  • 1 < = n u m s . l e n g t h < = 1 0 5 {1 <= nums.length <= 10^5} 1<=nums.length<=105
  • − 1 0 9 < = n u m s [ i ] < = 1 0 9 {-10^9 <= nums[i] <= 10^9} 109<=nums[i]<=109

思路讲述

可以使用动态规划得到乘积为正数的最长子数组长度。

首先我们定义两个数组positivenegative,其中positive表示以下标 i 结尾的乘积为正数的最长子数组长度,negative[i] 表示乘积为负数的最长子数组长度。

nums[0] > 0时,positive[0] = 1,当nums[0] < 0时,negative[0] = 1

对于任意下标i,当i大于1时,我们根据nums[i]来计算positive[i]negative[i] 的值。

第一种情况,当 nums[i] > 0时,之前的乘积乘以nums[i]之后不会改变计算后的正负性。

  • positive[i]的计算为:positive[i] = positive[i - 1] + 1;
  • negative[i]的计算为:
    negative[i - 1] > 0时,negative[i] = negative[i - 1] + 1;
    negative[i - 1] = 0时,negative[i] = 0

第二种情况,当 nums[i] < 0时,之前的乘积乘以nums[i]之后会改变计算后的正负性。

  • positive[i]的计算为:
    negative[i- 1] > 0时,positive[i] = negative[i - 1] + 1;
    negative[i - 1] = 0时,positive[i] = 0
  • negative[i]的计算为:negative[i] = positive[i - 1] + 1

第三种情况,当nums[0] = 0时,以下标i为结尾的元素乘积结果一定为0,因此positive[i] = negative[i] = 0

遍历结束完毕之后,我们便可以的出最长 子数组的长度。

实现

一、使用长度等于原数组长度的数组进行遍历。

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        int length = nums.size();
        vector<int> positive(length), negative(length);
        if (nums[0] > 0) {
            positive[0] = 1;
        }
        else if (nums[0] < 0) {
            negative[0] = 1;
        }
        int maxLength = positive[0];
        for (int i = 1; i < length; ++i) {
            if (nums[i] > 0) {
                positive[i] = positive[i - 1] + 1;
                negative[i] = (negative[i - 1] > 0 ? negative[i - 1] + 1 : 0);
            }
            else if (nums[i] < 0) {
                positive[i] = (negative[i - 1] > 0 ? negative[i - 1] + 1 : 0);
                negative[i] = positive[i - 1] + 1;
            }
            else {
                positive[i] = 0;
                negative[i] = 0;
            }
            maxLength = max(maxLength, positive[i]);
        }
        return maxLength;
    }
};

二、使用滚动数组实现。

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        int length = nums.size();
        int positive = (nums[0] > 0);
        int negative = (nums[0] < 0);
        int maxLength = positive;
        for (int i = 1; i < length; ++i) {
            if (nums[i] > 0) {
                positive ++;
                negative = (negative > 0 ? negative + 1 : 0);
            }
            else if (nums[i] < 0) {
                int newPositive = negative > 0 ? negative + 1 : 0;
                int newNegative = positive + 1;
                tie(positive, negative) = {newPositive, newNegative};
            }
            else {
                positive = negative = 0;
            }
            maxLength = max(maxLength, positive);
        }
        return maxLength;
    }
};

LeetCode 152. 乘积最大子数组

题目描述

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

测试用例的答案是一个 32-位 整数。

子数组 是数组的连续子序列。

示例 1:

输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。

示例 2:

输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

提示:

  • 1 < = n u m s . l e n g t h < = 2 ∗ 1 0 4 {1 <= nums.length <= 2 * 10^4} 1<=nums.length<=2104
  • − 10 < = n u m s [ i ] < = 10 {-10 <= nums[i] <= 10} 10<=nums[i]<=10
  • nums 的任何前缀或后缀的乘积都 保证 是一个 32-位 整数

思路讲述

我们使用两个数组来记录计算的的结果。其中f[i]维护计算过程中的大值数组,g[i]维护计算过程中的小值数组。

当下标i等于0f[0] = g[0] = nums[0]

对于任意下表i,我们情况进行考虑:

第一种情况:当nums[i] > 0时,f[i] = f[i - 1] * nums[i]g[i] = g[i - 1] * nums[i];
第二种情况:当nums[i] < 0时,f[i] = g[i - 1] * nums[i], g[i] = f[i - 1] * nums[i]
第三种情况:当nums[i] = 0时,f[i] = g[i] = 0

对于以上三种情况我们可以进行合并,使用fa来表示f[i - 1] * nums[i],使用ga来表示g[i - 1] * nums[i]

实现

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int res = nums[0];
        int f = nums[0], g = nums[0];
        for (int i = 1; i < nums.size(); i ++ ) {
            int a = nums[i], fa = f * a, ga = g * a;
            f = max(a, max(fa, ga));
            g = min(a, min(fa, ga));
            res = max(res, f);
        }
        return res;
    }
};

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