Maximum Product Subarray
Find the contiguous subarray within an array (containing at least one number) which has the largest product.
For example, given the array [2,3,-2,4]
,
the contiguous subarray [2,3]
has the largest product = 6
.
分析:我们可以通过下面的思路分析这道题,对于乘法运算array中的整数值可以分三类:0、正数、负数。当连续的subarray中包含0,则乘积为0,所以首先我们可以把0元素作为分隔符将array分成一系列subarray(当然代码实现并非先将array分成一系列subarray,这里这样表达便于理解)。然后在每个不含0的subarray中,我们可以这样考虑,如果第一个元素到当前元素共有偶数个负数,那么此subarray中最大成绩为从第一个元素到当前元素的乘积;如果第一个元素到当前元素共有奇数个负数,那么此subarray的最大乘积将在原来的max_product和此subarray第一个负数之后到当前元素段乘积中产生。实现算法是,遇到0可以更新一些标记量,从而可以使上述方法在O(n)时间复杂度完成。代码如下:
class Solution { public: int maxProduct(int A[], int n) { int maxP = INT_MIN; int from_start = 1, from_first_neg = 1; int neg_count = 0; for(int i = 0; i < n; i++){ if(A[i] == 0){ maxP = max(maxP,0); from_start = 1; from_first_neg = 1; neg_count = 0; }else{ from_start *= A[i]; if(neg_count > 0) from_first_neg *= A[i]; if(A[i] < 0) neg_count++; if(neg_count%2 == 0 || (neg_count == 1 && A[i] < 0))maxP = max(maxP,from_start); else if(neg_count > 0) maxP = max(maxP, from_first_neg); } } return maxP; } };
一个更简洁的写法,方法本质跟上面一样,但第一个负数之后的乘积是通过从后往前扫描的办法求得:
class Solution { public: int maxProduct(int A[], int n) { int b=1, f=1, res=INT_MIN; for(int i=0; i<n; i++){ res=max(res, max(b*=A[i],f*=A[n-1-i])); if(b==0) b=1; if(f==0) f=1; } return res; } };
此题也可以用动态规划的方法解,但递推公式有技巧性:
如果k之前没有负数,f(k)是largest product subarray from index 0 up to k. 如果k之前有负数,f(k)是第一个负数后从k的largest product subarray;g(k)为从0到k的最小乘积。如果A[k]为负,那么f(k)变为最小乘积,g(k)变为最大乘积;当f[k-1]=0或者g[k-1] = 0时,A[k]为最大值或者最小值。递推公式如下:
f(k) = max( f(k-1) * A[k], A[k], g(k-1) * A[k] ) g(k) = min( g(k-1) * A[k], A[k], f(k-1) * A[k] )
代码如下:
class Solution { public: int maxProduct(int A[], int n) { if(n == 0) return 0; int maxpre = A[0], minpre = A[0]; int maxP = A[0], maxhere, minhere; for(int i = 1; i < n; i++){ maxhere = max(max(maxpre*A[i], A[i]), minpre*A[i]); minhere = min(min(minpre*A[i], A[i]), maxpre*A[i]); maxP = max(maxP, maxhere); maxpre = maxhere; minpre = minhere; } return maxP; } };