题目来自编程之美
题目
举例
-4,-2,-3,-1 中任意三个数的最大乘积为-6
思路(1)暴力法
具体来说,把N个组合全找出来,计算乘积。时间复杂度为O(n^2).
思路(2)空间换时间,使用数据预先保存部分乘积的结果
具体来说:
使用数组nArrLeftPdt[i]:保存下标[0,i]之间数的乘积
使用数组nArrRightPdt[i]:保存下标[i,nLen - 1]之间数的乘积
则,去掉下标为i的元素的乘积为nArrLeftPdt[i] * nArrRightPdt[i].
这样,利用这两个数组,可以在O(n)的时间内得出结果。
代码:
#include <iostream>
#include <assert.h>
using namespace std;
long FindMaxProduct(int nArr[],int nLen)
{
assert(nArr && nLen > 0);
if (nLen == 1)
{
return -0x3f3f3f3f;
}
long* nArrLeftPdt = new long[nLen];
long* nArrRightPdt = new long[nLen];
//初始化
nArrLeftPdt[0] = nArr[0];
nArrRightPdt[nLen - 1] = nArr[nLen - 1];
for (int i = 1;i < nLen;i++)
{
nArrLeftPdt[i] = nArrLeftPdt[i - 1] * nArr[i];
nArrRightPdt[nLen - 1 - i] = nArrRightPdt[nLen - i] * nArr[nLen - 1 - i];
}
//比较
long nMax = nArrRightPdt[1];
for (int i = 1;i < nLen - 1;i++)
{
nMax = max(nMax,nArrLeftPdt[i - 1] * nArrRightPdt[i + 1]);
}
nMax = max(nMax,nArrLeftPdt[nLen - 2]);
return nMax;
}
int main()
{
int nLen = 4;
//int nArr[4] = {4,2,3,1};
//int nArr[4] = {4,-2,-3,1};
//int nArr[4] = {-4,-2,-3,-1};
//int nArr[4] = {4,-2,-3,-1};
//int nArr[4] = {4,2,3,-1};
//int nArr[4] = {4,0,0,-1};
//int nArr[4] = {4,-2,0,-1};
int nArr[4] = {4,-2,0,1};
cout<<FindMaxProduct(nArr,nLen)<<endl;
system("pause");
return 1;
}
思路(3)根据N个元素的乘积和正负0元素个数分析
(1)N个元素的乘积为0
<1>若剩余N-1个元素的乘积为0,则所有N-1个元素的组合的乘积肯定为0。
<2>若剩余N-1个元素的乘积为正数,则最大乘积为该正数。
<3>若剩余N-1个元素的乘积为负数,则最大乘积为0。
(2)N个元素的乘积为正数(原文有错误,少了一种情况)
<1>若所有元素均为正,则去掉最小的正数即可
<2>若所有元素均为负,则去掉绝对值最大的负数(最小的负数)即可
<3>若元素有正有负,则去掉最小的正数即可
(3)N个元素的乘积为负数
<1>若所有元素均为负,则去掉最大的负数(绝对值最小的负数)即可
<2>若部分元素为负,则去掉最大的负数(绝对值最小的负数)即可
为了尽量减少乘法的次数,我们可以只记录正数负数和0的个数以及最大的正数和最小的负数,之后分析元素个数得出结果。
时间复杂度:O(n)
代码:
#include <iostream>
#include <assert.h>
using namespace std;
//计算N-1个数之积
int FindProduct(int nArr[],int nLen,int nExceptNum)
{
bool bFlag = false;
int nPrt = 1;
for (int i = 0;i < nLen;i++)
{
if (!bFlag && nArr[i] == nExceptNum)
{
bFlag = true;
}
else
{
nPrt *= nArr[i];
}
}
if (nLen == 1)
{
return 0;
}
else
{
return nPrt;
}
}
int FindMaxProduct(int nArr[],int nLen)
{
assert(nArr && nLen > 0);
int nPosiCount = 0;
int nNegCount = 0;
int nZeroCount = 0;
int nMinPos = 0x3f3f3f3f;
int nMinNeg = 0x3f3f3f3f;
int nMaxNeg = -0x3f3f3f3f;
for (int i = 0;i < nLen;i++)
{
if (nArr[i] == 0)
{
nZeroCount++;
}
else if (nArr[i] > 0)//正数
{
nPosiCount++;
nMinPos = min(nMinPos,nArr[i]);
nPosiCount++;
}
else //负数
{
nMaxNeg = max(nMaxNeg,nArr[i]);
nMinNeg = min(nMinNeg,nArr[i]);
nNegCount++;
}
}
if (nZeroCount > 0)//N个元素乘积为0
{
if (nZeroCount > 1)
{
return 0;
}
else if ((nNegCount & 1) == 0) //偶数个负数,乘积为正数
{
return FindProduct(nArr,nLen,0);
}
else //奇数个负数,乘积最大为0
{
return 0;
}
}
else if (nNegCount & 1)//有奇数个负数,即所有数乘积为负数,去掉最大的负数
{
return FindProduct(nArr,nLen,nMaxNeg);
}
else //有偶数个负数,即所有数乘积为正数
{
if (nNegCount == nLen)//所有元素均为负,则去掉最小的负数
{
return FindProduct(nArr,nLen,nMinNeg);
}
else//所有元素均为正或有正有负,则去掉最小的正数
{
return FindProduct(nArr,nLen,nMinPos);
}
}
}
int main()
{
//int nLen = 4;
//int nArr[4] = {4,2,3,1};
//int nArr[4] = {4,-2,-3,1};
//int nArr[4] = {-4,-2,-3,-1};
//int nArr[4] = {4,-2,-3,-1};
//int nArr[4] = {4,2,3,-1};
//int nArr[4] = {4,0,0,-1};
//int nArr[4] = {4,-2,0,-1};
//int nArr[4] = {4,-2,0,1};
int nLen = 1;
//int nArr[1] = {0};
//int nArr[1] = {-1};
int nArr[1] = {1};
cout<<FindMaxProduct(nArr,nLen)<<endl;
system("pause");
return 1;
}