【算法拾遺】子數組的最大乘積

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章