少一个数的子序列的最大乘积

本文探讨了一种高效算法,用于从给定数组中找到n-1个数的组合,使得这些数的乘积最大,且整个计算过程不使用除法运算。通过分析数组中正数、负数和零的数量来确定最佳解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


问题描述:

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[]中的下标。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值