​DP8 乘积为正数的最长连续子数组-中等​

描述

给定一个长度为 n 的整数数组,请你找出其中最长的乘积为正数的子数组长度。

子数组的定义是原数组中一定长度的连续数字组成的数组。

数据范围: [动态规划]DP8 乘积为正数的最长连续子数组-中等_最大乘积 , 数组中的元素满足 [动态规划]DP8 乘积为正数的最长连续子数组-中等_最大乘积_02

输入描述:

第一行输入一个正整数 n ,表示数组长度。

第二行输入 n 个整数,表示数组中的元素。

输出描述:

输出最长的乘积为正数的子数组长度

示例1

输入:

5
1 2 3 -5 1

输出:

3

示例2

输入:

5
1 2 3 0 5

输出:

3

题解

动态规划解法

这个是​​DP7 连续子数组的最大乘积-中等​​变种,求最大乘积时我们使用dp数组存放的是乘积之和,求长度的时候,我们只需要用dp数组存放对应的长度即可。

步骤如下:

  1. 使用positive_dp[i]和negative_dp[i]分别表示以索引i结尾的时候乘积为正数和乘积为负数的最大长度
  2. positive_dp[0]初始化:如果v[0]大于0则取1,否则取0
  3. negative_dp[0]初始化:如果v[0]小于0则该值取1,否则取0
  4. 递推关系:
  1. 如果v[i] == 0,则positive_dp[i]=negative_dp[i]=0,因为0乘以任何数都为0,因此乘积长度为0
  2. 如果v[i]大于0,则p[i] = p[i-1]+1,因为正数只能和正数相乘得到正数,因此取上一个的最长正数长度+1;此时如果n[i-1]大于0表示前面的数相乘是负数,取n[i]=n[i-1]+1,否则n[i]=0
  3. 如果v[i]小于0,则n[i]=p[i-1]+1,因为要让乘积为负数最长,只能尽量长的取i-1为正数的情况;此时p[i]应该让负数的长度尽可能场,如果n[i-1]大于0则p[i]=n[i-1]+1,否则由于当前是负数,而i-1之前不是负数,因此乘积还是负数或者0,因此p[i]=0

步骤可能有点啰嗦,但实际上代码逻辑很简单~~

代码如下:

#include <bits/stdc++.h>

int64_t solve(std::vector<int64_t> &v)
{
if (v.size() == 1)
{
return v[0] > 0 ? 1 : 0;
}

// 分别表示以索引i为结尾时,最长乘积为正数的长度、最长乘积为负数的长度
std::vector<int64_t> positive_dp(v.size(), 0);
std::vector<int64_t> negative_dp(v.size(), 0);
int64_t ans = 0;
positive_dp[0] = v[0] > 0 ? 1 : 0;
negative_dp[0] = v[0] < 0 ? 1 : 0;
for (int i = 1; i < v.size(); ++i)
{
if (v[i] == 0) // 以i结尾的乘积为0,则正负dp的长度也是0
{
continue;
}

if (v[i] > 0)
{
positive_dp[i] = positive_dp[i - 1] + 1;
negative_dp[i] = negative_dp[i - 1] > 0 ? (negative_dp[i - 1] + 1) : 0;
}
else
{
positive_dp[i] = negative_dp[i - 1] > 0 ? (negative_dp[i - 1] + 1) : 0;
negative_dp[i] = positive_dp[i - 1] + 1;
}
ans = std::max(ans, positive_dp[i]);
}

return ans;
}
int main()
{
int n, x;
std::cin >> n;
std::vector<int64_t> v;
while (n-- > 0)
{
std::cin >> x;
v.push_back(x);
}

std::cout << solve(v) << std::endl;
return 0;
}