【演算法拾遺】子陣列的最大乘積

蘭亭風雨發表於2014-06-08

轉載請註明出處: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;
}

相關文章