问题:
给定一个长度为N的整数数组,只允许用乘法,不能用除法,计算任意(N-1)个数的组合中乘积最大的一组。
方法一:暴力枚举
把所有可能的N-1个数的组合求出来,分别计算他们的乘积。对于N个数而言,每一个数都需要计算N-1个数的成绩,于是复杂度为N(N-1)即O(N^2)的复杂度,显然不是最好的解法。
// 暴力枚举,需要访问N(N-1)个数
//返回那个没有被用到的数以及最大乘积
pair<int,int> findMaxMul(int Arr[] , int arrLen)
{
int maxMulSum = 0;
int index;//保存未用到的数的下标
for(int i=0 ; i<arrLen ; i++){//选择一个数剔除,计算其余的数的乘积
int mulSum = 1;
for(int j=0 ; j<arrLen ; j++){
if(j == i)
j++;
mulSum *=Arr[j];
}
if(mulSum > maxMulSum){
maxMulSum = mulSum;
index = i;
}
}
return make_pair(Arr[index] , maxMulSum);
}
int main()
{
int Arr[9] = {42,-5,6,-8,1,9,-5,33,12};
pair<int,int> p = findMaxMul(Arr , 9);
cout<<p.first<<" "<<p.second<<endl;
return 0;
}
方法二:空间换时间策略
用两个数组分别记录某个被剔除数据Arr[i]前后连乘积,S[i]数组表示数组前i个元素的连乘积,边界条件S[0]=1,可以知道S[1]=Arr[0],S[2]=Arr[0]*Arr[1]······,T[i]数组表示后(N-i)个元素的连乘积,边界条件T[N]=1,可以知道T[N-1]=Arr[N],T[N-2]=Arr[N]*Arr[N-1]······
于是可以得到对于剔除Arr[i]后的连乘积,可以这样求mulSum[i] = S[i-1]*T[i+2];下面是代码:
//用空间换时间的方法,设定前缀连乘积数组S[i]和后缀连乘积数组T[i]
//结果mulSum[i] = S[i]*T[i+2]; i的范围在[0,arrLen-1];
pair<int,int> findMaxMul(int Arr[] , int arrLen)
{
int *S = new int[arrLen+1];
int *T = new int[arrLen+1];
int *sumMul = new int[arrLen];
int maxMul = 0;
int index = -1;
//初始化数组S、T
S[0]=1;
T[arrLen]=1;
for(int i=1 ; i<arrLen+1 ; i++)
S[i] = S[i-1]*Arr[i-1];
for(int i=arrLen-1 ; i>=0 ; i--)
T[i] = T[i+1]*Arr[i];
for(int j=0 ; j<arrLen ; j++){
sumMul[j] = S[j]*T[j+1];
if(maxMul < sumMul[j]){
maxMul = sumMul[j];
index = j;
}
}
delete []S;
delete []T;
delete []sumMul;
return make_pair(Arr[index] , maxMul);
}
方法三:启发式方法(根据经验得出计算方法)
对N个数的乘积进行分析,用启发式的方式得到在满足乘积最大情况下要删去那个数。
1.数组中有多于一个零则最大乘积为0;
2.数组中只有一个零,而有奇数个负数,则最大乘积必定为0;
3.数组中只有一个零,而有偶数个负数,则最大乘积为除去0的元素的乘积;
4.数组中没有零,而有奇数个负数,则最大乘积为除去绝对值最小的负数的乘积;
5.数组中没有零,而有偶数个负数,则最大乘积为除去最小的正数的乘积。