子数组的最大乘积

题目:

     给定一个长度为N的整数数组,只允许使用乘法,不能用除法,计算任意N-1个数的组合中乘积中最大的一组,并写出算法的时间复杂度。

 

解法:

     实际上第一反应就是先将N个数相乘得到结果,然后遍历每一个数,看去掉哪个数最适合,注意正负号。效率O(N).

     但是文章在最后指出不允许用除法的用意是这样的,乘法很容易溢出。意思是干脆乘法也不要用。

文中给出如下几种算法:

源码出自:http://blog.csdn.net/qq120848369/archive/2010/05/12/5583796.aspx

算法一: 

       枚举每一个不在N-1内的数,分别计算剩余N-1个数的乘积, 由于有N种情况,每种情况遍历计算乘积,所以O(n2).

算法二:

      假设第i位不在N-1内,那么需要左边的乘积乘以右边的乘积.   为了优化时间,做预处理,先算出i位左边的乘积与右边的乘积.

这个过程需要2 *n的时间,是线性的。 计算Max也是扫描一遍就得到了,所以算法复杂度n,代码如下:

01.#include <iostream>   
02.using namespace std;   
03.  
04.//全局变量   
05.int *num;   
06.int *_left;   
07.int *_right;   
08.  
09.void getLeft(int len)   
10.{   
11.    _left[0]=1;   
12.    for(int i=1;i<len;++i)   
13.    {   
14.        _left[i]=_left[i-1]*num[i-1];   
15.    }   
16.}   
17.  
18.void getRight(int len)   
19.{   
20.    _right[len-1]=1;   
21.    for(int i=len-2;i>=0;--i)   
22.    {   
23.        _right[i]=_right[i+1]*num[i+1];   
24.    }   
25.}   
26.  
27.int getMax(int len,int &max)   
28.{   
29.    max=_left[0]*_right[0];   
30.    int pos=0;   
31.    for(int i=1;i<len;++i)   
32.    {   
33.        if(_left[i]*_right[i]>max)   
34.        {   
35.            max=_left[i]*_right[i];   
36.            pos=i;   
37.        }   
38.    }   
39.    return pos;   
40.}   
41.  
42.int main()   
43.{   
44.    int len;   
45.    cin>>len;   
46.    num=new int[len];   
47.    _left=new int[len];   
48.    _right=new int[len];   
49.    for(int i=0;i<len;++i)   
50.    {   
51.        cin>>num[i];   
52.    }   
53.    getLeft(len);   
54.    getRight(len);   
55.    int max;   
56.    cout<<"数组下标:"<<getMax(len,max)<<",最大值:";   
57.    cout<<max<<endl;   
58.    delete []num;   
59.    delete []_left;   
60.    delete []_right;   
61.    return 0;   
62.} 

算法3:

     计算N个数的乘积为P,然后分P的正负性讨论如下:

     1,P==0

           说明P中必定至少含有一个0。假设将这个0去掉后得到N-1个元素的乘积为Q。

           1.1 Q==0

                  返回0。说明N个元素中必有至少两个0,所以不管去掉什么元素,N-1个乘积必为0。

           1.2 Q为正

                  返回Q。说明其中再无0了,若之前去掉的不是0,则剩余的N-1个的乘积必为0。小于现在的Q。

            1.3 Q为负

                  返回0,。说明其中再无0了,若之前去掉的不是0,则剩余的N-1个的乘积必为0。大于现在的Q,取大者,所以之前应该保留0。

        2,P为负

              说明这N个数中无0,并且至少有一个负数。所以只有去掉一个绝对值最小的负数才获得最大乘积Q。并且这个负数必定是存在的。

        3,P为正

              由于可能负负得正,所以现在应该考虑到应该去掉一个绝对值最小的正数,但是这个正数不一定存在,比如数组-1,-1。所以如果该正数不存在,就应该去掉一个绝对值最大的负数。

 

同时注意,为了避免乘积溢出,建议只统计符号,计算0,正,负的个数 。            

代码:

#include "stdafx.h"
#include<iostream>
#include<stdio.h>
#include<time.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
#include<Windows.h>
using namespace std;
#define MAX 100
int M;
int arr[MAX];
int sig[3];//统计符号0,正,负的个数
int _tmain(int argc, _TCHAR* argv[])
{
 //处理输入
 cin>>M;
 for(int i=0;i<M;i++)
 {
  cin>>arr[i];
 }

 memset(sig,0,sizeof(int));
 for(int i=0;i<M;i++)
 {
  if(arr[i]==0)
  {
   sig[0]++;
  }
  else if(arr[i]>0)
  {
   sig[1]++;
  }
  else
  {
   sig[2]++;
  }
 }

 if(sig[0]>2||(sig[0]==1&&sig[2]%2==0))//情况1.1  1.2 去掉0
 {
  for(int i=0;i<M;i++)
  {
   if(arr[i]==0)
   {
    cout<<"位置:"<<i<<endl;
    break;
   }
  } 
 }
 else if(sig[0]==1)//情况1.3  Q为负数去掉任意一个都可以
 {
  for(int i=0;i<M;i++)
  {
   if(arr[i]!=0)
   {
    cout<<"位置:"<<i<<endl;
    break;
   }
  } 
 }
 else if(sig[2]%2==1)//情况2  去掉绝对值最小的负数
 {
  int min=INT_MIN;
  int pos=0;
  for(int i=0;i<M;i++)
  {
   if(arr[i]<0&&arr[i]>min)
   {
    min=arr[i];
    pos=i;

   }
  } 
  cout<<"位置:"<<pos<<endl;
 }
 else //情况3
 {
  //有正数
  if(sig[1]>0)
  {
   int max=INT_MAX;
   int pos=0;
   for(int i=0;i<M;i++)
   {
    if(arr[i]>0&&arr[i]<max)
    {
     max=arr[i];
     pos=i;

    }
   } 
   cout<<"位置:"<<pos<<endl;
  }
  else 
  {
   int min=0;
   int pos=0;
   for(int i=0;i<M;i++)
   {
    if(arr[i]>0&&abs(arr[i])>min)
    {
     min=abs(arr[i]);
     pos=i;
    }
   } 
   cout<<"位置:"<<pos<<endl;
  }
 }

 ::system("pause");

 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值