题目描述:
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.
Hide Tags Array Dynamic Programming
分析:
之前做过求最大连续子串的和,采用了动态规划的思想,从前往后到每个元素的最大和。
而此题是给定一个至少含有一个元素的无序整型数组,要求求出最大连续子串的乘积。
方法1、想得比较复杂:
同样是动态规划的思想:由于乘积会受0元素和负数元素的影响,所以最好是先跳过0元素,将序列先按照0元素分成多个片段,
然后计算每个非零序列的最大子串乘积,这就划分为多个子问题了。而要求一个非零序列的最大子串乘积,需要考虑负数元素的影响:
1 如果该非零序列中的负数元素个数是偶数,那么问题就简单化了,直接将该序列中的所以非零元素相乘即可。
2 如果非零序列中含有奇数个负数元素:
2.1 如果含有1个负数,则用该元素将序列划分为两个子序列(均不包含该负数),然后分别计算两个子序列的乘积,返回较大者即可。
2.2 如果含有3个及以上负数,则要进行两次分割,即利用第一个负数和最后一个负数分别分割一次(过程和2.1 完全一样)。
然后返回两次分割中的较大者即可。
在求得每个非零序列的最大子串乘积后,更新整个数组的最大子串乘积为max。
最后max是负数,并且数组中存在0元素,那么max应该更新为0。否则不用更新,直接返回max。
实现过程中,为了方便函数调用,会虚拟A[-1]和A[A.size()]是0,但是在计算非零序列的最大子串乘积时要注意下标的变化,以免越界。
方法2、动态规划
由于可能出现负数,如果当前的元素也刚好是负数,那么久负负得正了。因此需要一个变量保存当前的最小值,那么状态转换方程如下:
当前下标i为最末元素的最大子段乘积为 max_tmp = max(A[i],最小值*A[i],最大值*A[i])
以下是C++实现代码,方法1虽然思路比较复杂,但是效率却比动态规划要高。
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.
Hide Tags Array Dynamic Programming
分析:
之前做过求最大连续子串的和,采用了动态规划的思想,从前往后到每个元素的最大和。
而此题是给定一个至少含有一个元素的无序整型数组,要求求出最大连续子串的乘积。
方法1、想得比较复杂:
同样是动态规划的思想:由于乘积会受0元素和负数元素的影响,所以最好是先跳过0元素,将序列先按照0元素分成多个片段,
然后计算每个非零序列的最大子串乘积,这就划分为多个子问题了。而要求一个非零序列的最大子串乘积,需要考虑负数元素的影响:
1 如果该非零序列中的负数元素个数是偶数,那么问题就简单化了,直接将该序列中的所以非零元素相乘即可。
2 如果非零序列中含有奇数个负数元素:
2.1 如果含有1个负数,则用该元素将序列划分为两个子序列(均不包含该负数),然后分别计算两个子序列的乘积,返回较大者即可。
2.2 如果含有3个及以上负数,则要进行两次分割,即利用第一个负数和最后一个负数分别分割一次(过程和2.1 完全一样)。
然后返回两次分割中的较大者即可。
在求得每个非零序列的最大子串乘积后,更新整个数组的最大子串乘积为max。
最后max是负数,并且数组中存在0元素,那么max应该更新为0。否则不用更新,直接返回max。
实现过程中,为了方便函数调用,会虚拟A[-1]和A[A.size()]是0,但是在计算非零序列的最大子串乘积时要注意下标的变化,以免越界。
方法2、动态规划
由于可能出现负数,如果当前的元素也刚好是负数,那么久负负得正了。因此需要一个变量保存当前的最小值,那么状态转换方程如下:
当前下标i为最末元素的最大子段乘积为 max_tmp = max(A[i],最小值*A[i],最大值*A[i])
以下是C++实现代码,方法1虽然思路比较复杂,但是效率却比动态规划要高。
附带注释,代码有点长:
class Solution {
public:
int getmax_Product(vector<int> A,int lef,int rig,int neg_index){
/**计算以下标neg_index分割(lef,rig)为两边的乘积的较大者,lef和rig分别是A中0元素对应的下标,neg_index是负数的下标*/
int max = INT_MIN; // 最大乘积初始化为最小整数
/**计算区间(lef,neg_index)的元素的乘积,不含0元素,且含偶数个负数*/
int p1 = A[lef + 1];
int i = lef + 2;
while( i < neg_index){
p1 *=A[i];
i++;
}
if(max < p1) //更新最大乘积
max = p1;
/**计算区间(neg_index,rig)的元素的乘积,不含0元素,且含偶数个负数*/
int p2 = A[rig - 1];
i = rig - 2;
while(i > neg_index){
p2 *=A[i];
i--;
}
if(max < p2)
max = p2;
return max;
}
int pro_neg(vector<int> A,int lef,int rig){
//计算区间(lef,rig)的最大乘积,lef和rig分别是A中0元素对应的下标
if(lef + 1 == rig ) //如果是两个连续的0元素,则直接返回结果0
return 0;
int neg_i = -1; //区间内第一个负数的下标,初始化为-1
int neg_j = -1; //区间中最后一个负数的下标。初始化为-1
int neg_cnt = 0; //统计区间内负数的个数
for(int i = lef + 1; i < rig; i++){ //寻找第一个负数元素
if(A[i] < 0){
neg_i = i;
neg_cnt++;
break;
}
}
for(int i = neg_i + 1; i < rig; i++){ //寻找最后一个负数元素
if(A[i] < 0){
neg_j = i;
neg_cnt++;
}
}
/**当区间中含有非零元素,进行以下处理*/
if(neg_cnt % 2 == 0 || neg_i == -1){
//如果区间内负数个数为偶数,直接将(lef,rig)之间的元素相乘,结果必然是区间的最大乘积
int p = 1;
//compute product of (lef,rig)
for(int i = lef + 1; i < rig; i++){
p *= A[i];
}
return p;
}
else{ //区间的的负数个数为奇数
if(neg_j == -1){ //只含有一个奇数,只需一次划分计算
return getmax_Product(A,lef,rig,neg_i);
}
else{
//含有至少三个负数,那么要进行两次划分,分别以第一个和最后一个负数的下标将区间划分,得到最大值返回
int p1 = 0, p2 = 0;
p1 = getmax_Product(A,lef,rig,neg_i);
p2 = getmax_Product(A,lef,rig,neg_j);
return p1 > p2 ? p1:p2;
}
}
}
int maxProduct(vector<int> A){
int n = A.size();
if(n == 1) //只含有一个元素,则直接返回
return A[0];
int max = INT_MIN;
vector<int> zero_i;
zero_i.push_back(-1); //在A的两端模拟是0元素,以便于调用pro_neg(),以免A中首尾不全是0造成调用失败
for(int i = 0; i != n; i++){ //将A中0元素的下标放入数组zero_i中
if(A[i] == 0)
zero_i.push_back(i);
}
zero_i.push_back(n); //在A的两端模拟是0元素,以便于调用pro_neg(),以免A中首尾不全是0造成调用失败
if(zero_i.size() == 2+n) //判断A中是否全是0
max = 0;
int p = 0;
for(int i = 1; i != zero_i.size(); i++){ //计算每两个0元素之间的元素的最大乘积,同时更新整个数组的连续序列的最大乘积
p = pro_neg(A,zero_i[i-1],zero_i[i]); //计算区间(zero[i-1],zero_i[i])不含有0的序列的最大乘积,注意不包含区间首尾元素
//cout << i << ":" << p << endl;
if(max < p)
max = p;
}
if(zero_i.size() > 2 && max < 0) //如果每个区间中的最大乘积是负数,并且A中含有0元素,所以应该更新max为0
max = 0;
return max;
}
};
/**/方法2///8ms///*/
class Solution {
public:
int maxProduct(vector<int> A) {
int size= A.size();
if(size == 0)
return 0;
if(size == 1)
return A[0];
int max_tmp = A[0]; // 记录当前最大值
int min_tmp = A[0];//记录当前最小值
int max_p = A[0];
for(int i = 1; i < size; i++){
int tmp = min_tmp; //每次迭代都要更新当前的最大值和最小值,要注意先保存旧值
min_tmp = min(A[i],min(tmp * A[i], max_tmp * A[i]));
max_tmp = max(A[i],max(tmp * A[i], max_tmp * A[i]));
max_p = max(max_p,max_tmp); //更新全局最大值
}
return max_p;
}
};