【算法拾遗】子数组的最大乘积

转载请注明出处:http://blog.csdn.net/ns_code/article/details/29224185


    给定一个长度为N的整数数组,只允许使用乘法,不能使用除法,计算任意N-1个数的组合乘积的最大值。


    这道题目重点要注意数组中有负数、0的情况。最直观的做法就是把所有可能的N-1个数的组合找出来,分别计算他们的乘积,并比较大小。找出所有组合需要O(N)时间,计算每个组合的乘积需要O(N)时间,因此该算法的时间复杂度为O(N*N)。

    编程之美上给出了两种O(N)的解法。

    第一种比较直观,假设去掉第i个元素后的剩下的N-1个元素的成绩为p[i],则从左向右扫描数组,计算第0到第i-1个元素的乘积s[i],再从右向左扫描数组,计算从第N-1个元素到第i+1个元素的乘积t[i],二者相乘便是除去第i个元素的剩下N-1个元素的乘积p[i],而后比较所有的p[i]即可。由于每次计算s[i+1]和t[i-1]时直接可以利用s[i]和t[i]的结果,因此扫描一次过去的时间复杂度为O(N),找出p[i]的最大值也是O(N),因此时间复杂度为O(N)。

    第二种方法,将问题转化到对去掉的那个元素的分析上,只在最后计算一次乘积即可。这种方法要先扫描一次数组,得到数组中正整数的个数、负整数的个数、0的个数,最小的正整数、绝对值最大的负整数和绝对值最小的负整数。而后详细地根据数组中正负数以及0的个数来判断要剔除的元素。

    1、如果数组中0的个数大于1,则任意N-1个元素的乘积都为0,去掉任一元素均可;

    2、如果数组中0的个数为1,则需要分两种情况;

    {

1、如果数组中负数的个数为偶数个,此时去掉0,剩下的N-1个数的乘积最大,为正值;

2、如果数组中负数的个数为奇数个,此时N-1个数的乘积最大值为0,去掉任意一个非0元素即可。

    }    

    3、如果数组中没有0,则需要分两种情况:

    {

1、如果数组中的负数个数为奇数个,此时去掉一个负数后的剩下N-1个数的乘积为正值,要保证这个正值最大,我们需要去掉绝对值最小的负            数,即最大的负数;

     2、如果数组中的负数个数为偶数个,则需要分两种情况:

     {

    1、如果数组中没有正整数,则去掉一个负数后,剩下的N-1个数的乘积为负值,要保证这个负值最大,我们需要去掉绝对值大的负数 ,                即最小的负数;

         2、如果数组中有正整数,则去掉最小的正整数,剩下的N-1个元素的乘积即为最大的。

}

    }

    按照这种思路实现的代码如下:

bool flag;
long long MaxProduct(int *arr,int len)
{
	if(arr==NULL || len<1)
	{ 
		flag = false;
		return 0;
	}

	int minus = 0;	//负数个数
	int plus = 0;	//正数个数
	int zero = 0;	//0的个数
	int minAbsMinus = (signed int)0x80000000;	//绝对值最小的负整数,先初始化为最小的int负数
	int maxAbsMinus = -1;						//绝对值最大的负整数,先初始化为最大的负整数
	int minPlus = 0x7FFFFFFF;					//最小的正整数,先初始化为最大的int正数

	int i;
	for(i=0;i<len;i++)
	{
		if(arr[i] == 0)
			zero++;
		else if(arr[i] < 0)
			minus++;
		else
			plus++;

		if(arr[i]<0 && arr[i]>minAbsMinus)
			minAbsMinus = arr[i];
		if(arr[i]<0 && arr[i]<maxAbsMinus)
			maxAbsMinus = arr[i];
		if(arr[i]>0 && arr[i]<minPlus)
			minPlus = arr[i];	
	}

	int outNum;		//不参与乘积的数
	long long result = 1;	//n-1个数的最大乘积

	//0的个数大于1的情况,这时任意n-1个数的乘积都为0,
	if(zero > 1)
		return 0;
	//如果有一个0,则需要根据正负数的个数来决定
	if(zero == 1)
	{
		//如果负数的个数为偶数个,
		//则去掉0后的n-1个数的乘积为正,即为最大值
		if((minus&1) == 0)
			outNum = 0;
		//如果负数的个数为奇数个,
		//则去掉0后的n-1个数的乘积为负,因此最大值应该为0,
		//去掉任一个非0元素即可
		else	
			return 0;
	}
	//如果没有0,则需要根据正负数的个数来决定
	else
	{
		//如果负数个数为奇数个,则去掉一个负数后,剩下的n-1个元素的乘积为正,
		//此时去掉绝对值最小的负数,剩下的n-1个数的乘积便最大
		if((minus&1) != 0)
			outNum = minAbsMinus;
		//如果负数个数为偶数个,这时候要分两种情况,
		//数组中有正数和没正数
		else
		{
			//如果数组中没有正数,则n-1个负数的乘积肯定为负数,
			//去掉绝对值最大的负数,便可得n-1个负数乘积的最大值
			if(plus == 0)
					outNum = maxAbsMinus;
			//如果数组中有正数,则去掉最小的正数,便可得n-1个数乘积最大值
			else
				outNum = minPlus;
		}
	}

	//计算乘积
	for(i=0;i<len;i++)
	{
		if(arr[i] != outNum)
			result *= arr[i];
	}
	
	return result;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值