本题可以通过动态规划建表,在O(N) 时间内解决。
表中的元素
d
p
[
i
]
dp[i]
dp[i] 代表以第
i
i
i 个数结尾的连续子数字的最大和。由于子数组是连续的,第
i
i
i 个元素为结尾仅有两种,要么是其本身,要么是在前一个为元素结尾的基础上加上其本身,二者取最大值。因此可写出状态转移方程:
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
+
n
u
m
s
[
i
]
,
n
u
m
s
[
i
]
)
dp[i] = max(dp[i] + nums[i], nums[i])
dp[i]=max(dp[i]+nums[i],nums[i])附上代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
vector<int> dp(nums.size(), nums[0]);
int maximum = nums[0];
for(int i=1; i<nums.size(); i++) {
dp[i] = max(dp[i-1]+nums[i], nums[i]);
maximum = max(maximum, dp[i]);
}
return maximum;
}
};
下面一个题,把和换成了乘积。
可能一看这个题就会想到:哎,这不和刚才那个差不多,建表存储以第
i
i
i个数为结尾的乘积较大值,统计输出最大值就完事了!
然而,这样做是完全不对的!!
在加法中,负数无论如何都只会对总和产生负面的影响,
而在乘法中,存在负负得正的情况。负数遇到新的负数乘积会变成更大的正数。
除dp建表之外,还可以使用两次遍历(正向遍历+反向遍历)解决该问题。
由于所有的数组都为整数,若不看正负只看绝对值的情况,每乘新的数字都不会使总乘积变小(0除外)即
若数组
n
u
m
s
nums
nums中不存在零,且负数的个数为偶数个时,将所有数相乘时得到最大值。
反之,若负数的个数为奇数,则在某一个负数的位置“切一刀”,两边的乘积较大的一个即为最大值。
’切一刀‘之后,最大值的取得总是从数组的两端开始乘起的,因此可以使用正反两次遍历累乘得到。
初始将
c
u
r
r
e
n
t
current
current 设为1,不断与遇到的数字相乘,记录最大值。若遇到零,
c
u
r
r
e
n
t
current
current 设为1继续遍历。
附上代码:
class Solution {
public:
int maxProduct(vector<int>& nums) {
int current = 1, maximum = INT_MIN;
for(int i=0; i<nums.size(); i++) {
current *= nums[i];
maximum = max(maximum, current);
if(nums[i] == 0) current = 1;
}
current = 1;
for(int i = nums.size()-1; i>=0; i--) {
current *= nums[i];
maximum = max(maximum, current);
if(nums[i] == 0) current = 1;
}
return maximum;
}
};
另外一种较为传统的动态规划算法,用两个数组,分别记录了以第
i
i
i 个数为结尾的乘积的最大值和最小值,用来应对负负得正的情况,状态转移方程为:
d
p
m
a
x
[
i
]
=
m
a
x
(
n
u
m
s
[
i
]
×
d
p
m
a
x
[
i
−
1
]
,
n
u
m
s
[
i
]
×
d
p
m
i
n
[
i
]
,
n
u
m
s
[
i
]
)
dp_{max}[i] = max(nums[i]\times dp_{max}[i-1], nums[i]\times dp_{min}[i], nums[i])
dpmax[i]=max(nums[i]×dpmax[i−1],nums[i]×dpmin[i],nums[i])
d
p
m
i
n
[
i
]
=
m
i
n
(
n
u
m
s
[
i
]
×
d
p
m
a
x
[
i
−
1
]
,
n
u
m
s
[
i
]
×
d
p
m
i
n
[
i
−
1
]
,
n
u
m
s
[
i
]
)
dp_{min}[i] = min(nums[i]\times dp_{max}[i-1], nums[i]\times dp_{min}[i-1], nums[i])
dpmin[i]=min(nums[i]×dpmax[i−1],nums[i]×dpmin[i−1],nums[i])附上代码:
class Solution {
public:
int maxProduct(vector<int>& nums) {
int maximum;
vector<int> maxi(nums.size(), nums[0]);
vector<int> mini(nums.size(), nums[0]);
for(int i=1; i<nums.size(); i++) {
maxi[i] = max(nums[i], nums[i]*maxi[i-1]);
maxi[i] = max(maxi[i], mini[i-1]*nums[i]);
mini[i] = min(nums[i], nums[i]*mini[i-1]);
mini[i] = min(mini[i], maxi[i-1]*nums[i]);
}
return *max_element(maxi.begin(), maxi.end());
}
};