DP7 连续子数组的最大乘积
描述
输入一个长度为 n 的整型数组 nums,数组中的一个或连续多个整数组成一个子数组。求所有子数组的乘积的最大值。
1.子数组是连续的,且最小长度为 1 ,最大长度为 n
2.长度为 1 的子数组,乘积视为其本身,比如 [4] 的乘积为 4
3.该题的数据保证最大的乘积不会超过 int 的范围,即不超过
数据范围:
输入描述:
第一行输入一个正整数 n ,表示数组的长度
第二行输入 n 个整数,表示数组中的值。
输出描述:
输出子数组的乘积的最大值
示例1
输入:
4
3 2 -2 4
输出:
6
说明:
子数组[3,2]的乘积为6,[3,2,-1,4]的乘积为-24,[4]的乘积为4,故返回6
示例2
输入:
3
-3 0 -2
输出:
0
说明:
因为0在中间,所有包含0的子数组的乘积都为0,另外的数都是负数,所以最大乘积的子数组为[0],返回为0,因为子数组要求是连续的,所以[-3,-2]不是[-3,0,-2]的子数组,所以不能为6,
示例3
输入:
3
-3 2 -2
输出:
12
题解
对于求最值问题,我们首先想到的就是动态规划。
- 假设输入数据为数组v
- 先定义dp数组,假设dp[i]表示的是以索引i为结束的时候最大的连续子数组的乘积
- 初始条件:dp[0] = v[0]
- 状态转移:
- 如果v[i]等于0,则dp[i] = 0
- 如果v[i]大于0,则dp[i]=max(v[i],dp[i-1]*v[i])
- 如果v[i]小于0,则dp[i]=min(v[i],dp[i-1]*v[i])
上面的状态转移看起来很美好,但实际上存在一个问题,那就是如果v[i]大于0,则dp[i-1]应该为正数且应该尽可能的大;如果v[i]小于0,那么dp[i-1]应该取负数,且应该尽可能的小。
根据上面的情况,我们需要先求出dp[i-1]的2种情况——也就是最大正数乘积或者最小负数乘积,然后再根据v[i]的值来做选择。因此,我们实际上需要2个dp数组,一个用来存放最大的正数,一个用来存放最小的负数。
步骤如下:
- 定义2个dp数组max_dp[i]和min_dp[i],分别表示以索引i结束的时候最大乘积以及最小乘积
- 初始条件:min_dp[0]=max_dp[0]=0
- 推导条件:
- 如果v[i]等于0,则max_dp[i]=min_dp[i]=0
- 如果v[i]大于0,则max_dp[i]=max(vp[i],max_dp[i-1]*v[i]);min_dp[i]=min(v[i],v[i]*min_dp[i-1])
- 如果v[i]小于0,则max_dp[i]=max(vp[i],min_dp[i-1]*v[i]);min_dp[i]=min(v[i],v[i]*max_dp[i-1])
代码如下:
#include <bits/stdc++.h>
int64_t solve(std::vector<int64_t> &v)
{
if (v.size() == 1)
{
return v[0];
}
std::vector<int64_t> max_dp(v.size(), 0);
std::vector<int64_t> min_dp(v.size(), 0);
max_dp[0] = min_dp[0] = v[0];
int64_t ans = v[0];
for (int i = 1; i < v.size(); ++i)
{
if (v[i] == 0)
{
max_dp[i] = min_dp[i] = 0;
}
else if (v[i] > 0)
{
max_dp[i] = std::max(v[i], max_dp[i - 1] * v[i]);
min_dp[i] = std::min(v[i], min_dp[i - 1] * v[i]);
}
else
{
max_dp[i] = std::max(v[i], min_dp[i - 1] * v[i]);
min_dp[i] = std::min(v[i], max_dp[i - 1] * v[i]);
}
ans = std::max(max_dp[i], ans);
}
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;
}