藍橋杯第九屆C++省B組[第10題:乘積最大] —— 貪心算法

參考博客

去年參加藍橋杯本來自我感覺良好,結果做題的時候思路亂的要死,最簡單的方法能夠解決的非想的複雜,導致做到最後一題的時候早已心煩意亂。此次再次看到這個題目不得不說作爲最後一個大題來說複雜度倒是比較高的(雖然只是貪心 ),最後花了大概1個多小時理清了本題的思路並解決了,估計比賽要花2 個小時。所以自己還是太弱了主要是這類題做得比較少把

題目:

給定N個整數A1, A2, … AN。請你從中選出K個數,使其乘積最大。
請你求出最大的乘積,由於乘積可能超出整型範圍,你只需輸出乘積除以1000000009的餘數。

注意,如果X<0, 我們定義X除以1000000009的餘數是負(-X)除以1000000009的餘數。即:0-((0-x) % 1000000009)
【輸入格式】
第一行包含兩個整數N和K。
以下N行每行一個整數Ai。

對於40%的數據,1 <= K <= N <= 100
對於60%的數據,1 <= K <= 1000
對於100%的數據,1 <= K <= N <= 100000 -100000 <= Ai <= 100000
注:對於100000的數據來說,相當於只要你時間複雜度小於 n ^ 3就沒問題。藍橋杯省賽對優化 這方面放得比較鬆 。而且下面給出的時間複雜度大概是(n * log(n) + k + n 是小於n * n的完全可以AC)

【輸出格式】
一個整數,表示答案。

【輸入樣例】
5 3
-100000
-10000
2
100000
10000

【輸出樣例】
999100009

再例如:
【輸入樣例】
5 3
-100000
-100000
-2
-100000
-100000

【輸出樣例】
-999999829

站在取到的k個數的角度,分情況經行考慮:

// 對於取到的k個數中
	// 1. 如果必須選0, 那麼就輸出0
	// 2. 如果前k個數中負數爲偶數個,那麼就直接計算輸出
	// 3. 如果必須選取奇數個負數, 那麼就選取最小的, 即n全部爲負數
	// 4. 如果前k個數中負數爲奇數個, 
	// 那麼就可以將一個小負數替換成大正數,或者將小正數替成大負數,取最大 
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
long long MAX = 1000000009; 

long long num[10005] = {};

int k = 0;// 選出k個數 
int n = 0;// 共n個數 

int fu = 0;// 記錄選取的負數的個數 
int frist_z_index = 0;	// 記錄第一個正數的位置 

int cmp(const void* a, const void* b){
	return abs(*(int*)b) - abs(*(int*)a);
} 

int main(){
	
	
	scanf("%d", &n);
	scanf("%d", &k);
	long long temp = 0;
	for(int i = 0; i < n; ++i){
		scanf("%lld", &temp);
		num[i] = temp;
	}
	qsort(num, n, sizeof(num[0]), cmp);// 由絕對值從大到小排序
 
	// 1. 如果必須選0, 那麼就輸出0
	// 2. 如果前k個數中負數爲偶數個,那麼就直接輸出
	// 3. 如果必須選取奇數個負數, 那麼就選取最小的, 即n全部爲負數
	// 4. 如果前k個數中負數爲奇數個, 
	// 那麼就可以將一個小負數替換成大正數,或者將小正數替成大負數,取最大 
  
	
	for(int i = 0; i < k; ++i){
		if(num[i] == 0){// 第1種情況  必須要選0 
		/*測數輸入樣例:
			5 3
			3 4 0 0 0
		*/
			printf("第1種情況: 0");
			return 0;
		}else if(num[i] < 0){
			++fu;// 記錄負數多少個
		} 
	}
	for(int j = 0; j < n; ++j){
		if(num[j] >= 0){
			frist_z_index = j;// 尋找是否還存在正數可以替換 
			break;
		}
	}
	
	if(fu & 1){// 如果是奇數個負數 
		if(frist_z_index != 0){ // 第4種情況, 可以替換成偶數個負數 
		/**測數輸入樣例 
			5 3
			-1000 100 10 5 -2
			4 3
			-1000 100 10 -2
		*/
			int z1 = 0;
			int z2 = 0;
			int f1 = 0;
			int f2 = 0;
			// 記錄選擇中的最小的正負數,  未選擇的最大正負數。 
			for(int i = 0; i < n; i++){
				if(i < k){ 
					if(num[i] > 0){
						z1 = i;
					}else{
						f1 = i;
					}
				}else{
					if(num[i] > 0){
						if(!z2)
							z2 = i;
					}else{
						if(!f2)
							f2 = i;
					}
				}
			}
			if(!z2){// 如果不存在可替換的正數或者負數,那麼就好辦了 
				num[z1] = num[f2];
			} else if(!f2){
				num[f1] = num[z2];
			} else {
				if(abs(num[f1]) - num[z2] > num[z1] - abs(num[f2])){// 如果替換負數結果大 
					num[z1] = num[f2];  
				}else{// 替換正數結果大 
					num[f1] = num[z2];
				}
			}
			long long ans = 1L;
			for(int i = 0; i < k; i++){
				ans *= num[i];
				ans %= MAX;
			}
			printf("第4種情況: %lld", ans);
		}else{
			// n個數都爲負數 
			// 奇數個負數  
			// 第3種情況 
			/*測試輸入樣例
			5 3
			-100 -10 -1 -1 -1
			*/
			long long ans = 1L;
			for(int j = n - 1, count = 0; count < k; --j){
				ans *= num[j];
				ans = 0 - ((0 - ans) % MAX);
				++count;
			}
			printf("第3種情況: %lld", ans);
		}
	}else{// 第2種情況 偶數個負數 
	/*測數輸入樣例
		5 3
		-1 -1 -1 -2 2
	*/
		long long ans = 1L; 
		for(int i = 0; i < k; ++i){
			ans *= num[i];
			ans %= MAX;
		} 
		printf("第2種情況: %lld", ans);
	} 
	
	

	return 0;
}








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