问题描述:
n个数组成的一个序列,求任意(n - 1)个数的组合中乘积最大的一组,规定不能用除法。这个问题来自<编程之美>。
这n个数存于数组A[0..n-1]中。
1 解法一
设s[i]为数组A[]中前i个数的乘积,即
s[i] = 1 i = 0时,
s[i] = s[i - 1] * A[i] i = 1,2,...,n
设t[i]为数组A[]中后 n-i 个数的乘积,即
t[i] = 1 i = n时,
t[i] = t[i + 1] * A[i] i = n-1,n-2,...,0
设p[i]为处A[i]元素外,其余 n-1 个元素的乘积,则
p[i] = s[i] * t[i + 1] i = 0,1,...,n-1
由此可得出下面的算法实现:
int maxProduct(int A[], int n)
{
int s[n + 1], t[n + 1],p[n], i, max;
for (s[0] = 1, i = 1;i <= n;i++) //计算s[i],时间为O(n)
s[i] = s[i -1] * A[i - 1];
for (t[n] = 1, i = n - 1;i >= 0;i--) //计算t[i],时间为O(n)
t[i] = t[i + 1] * A[i];
for (i = 0;i < n;i++) //计算p[i],时间为O(n)
p[i] = s[i] * t[i + 1];
for (max = p[0], i = 1;i < n;i++) //找出最大的乘积,时间O(n)
if (p[i] > max)
max = p[i];
return max;
}
总的时间复杂度为O(n),算法有很多不足,比如计算了很多此乘法,计算机需要花大量的时间去作乘法操作。
2 解法二
设P为A[]中n个数的乘积,A(n-1)表示n-1个数的组合,P(n-1)表示n-1个数组合的乘积。对P的正负性进行分析:
(1) P = 0。则这n个数中至少有一个数为0,除去这个0,设其余n-1个数的乘积为Q,若:
(a) Q = 0 return 0
(b) Q > 0 return Q
(c) Q < 0 return 0
(2) P < 0。去掉一个负数,这个负数是数组A[]中所有负数之中最大的(绝对值最小的),其余n-1个数的乘积就是所求。
(3) P > 0。则:
(a) 若数组中有正数,则去掉数组中所有正数之中最小的一个,其余n-1个数的乘积即是所求。
(b) 若数组中无正数,去掉数组中最小的负数(绝对值最大的),其余n-1个数的乘积即是所求。
其实不需要求出P的值,而只需要统计数组A[]中正数、负数、零的个数即可分析问题了。
算法如下:
int improvedMaxProduct(int A[], int n)
{
int positive, zero, nagative, i, temp, k;
positive = zero = nagative = 0;
for (i = 0;i < n;i++)
if (A[i] > 0)
positive++;
else if (A[i] == 0)
zero++;
else
nagative++;
//printf("positive = %d, zero = %d, nagative = %d\n", positive, zero, nagative);
if (zero >= 2) //数组中有至少两个零,返回-1(代表结果是零)
return -1;
if (zero == 1 && nagative % 2 == 1) //数组中有一个零,但剩下的n-1个数的乘积是负值,返回-1
return -1;
if (zero == 1 && nagative % 2 == 0) { //数组中有一个零,但剩下的n-1个数的乘积是正值,去掉这个零
for (i = 0;i < n;i++)
if (A[i] == 0)
return i;
}
//之后是数组中没有零的情况
if (nagative % 2 == 1) { //P < 0
for (i = 0;A[i] > 0;i++)
continue;
for (temp = A[i], k = i, ++i;i < n;i++)
if (A[i] < 0 && A[i] > temp) {
temp = A[i];
k = i;
}
return k;
} //if
//p > 0
if (positive > 0) { //数组中有正数
for (i = 0;A[i] < 0;i++)
continue;
for (temp = A[i], k = i, ++i;i < n;i++)
if (A[i] > 0 && A[i] < temp) {
temp = A[i];
k = i;
}
return k;
} else { //数组中没有正数
for (temp = A[0], k = 0, i = 1;i < n;i++)
if (A[i] < temp) {
temp = A[i];
k = i;
}
return k;
}
}
返回值更改了,当最大乘积是0时,函数返回-1;否则返回去掉的那个数在数组A[]中的下标。