2019年第十屆藍橋杯【C++省賽B組】【第十題:靈能傳輸】——附解題思路及代碼

藍橋杯歷屆題目及解析彙總(附思路及代碼)【點擊此進入】


藍橋杯,ACM算法學習【文檔】【視頻】大放送【點擊此進入】


第十題

標題:靈能傳輸(時間限制: 1.0s 內存限制: 256.0MB 本題總分:25 分)

【題目背景】
在遊戲《星際爭霸 II》中,高階聖堂武士作爲星靈的重要 AOE 單位,在
遊戲的中後期發揮着重要的作用,其技能”靈能風暴“可以消耗大量的靈能對
一片區域內的敵軍造成毀滅性的傷害。經常用於對抗人類的生化部隊和蟲族的
刺蛇飛龍等低血量單位。
【問題描述】
你控制着 n 名高階聖堂武士,方便起見標爲 1,2,··· ,n。每名高階聖堂武士
需要一定的靈能來戰鬥,每個人有一個靈能值 a i 表示其擁有的靈能的多少(a i
非負表示這名高階聖堂武士比在最佳狀態下多餘了 a i 點靈能,a i 爲負則表示這
名高階聖堂武士還需要 −a i 點靈能才能到達最佳戰鬥狀態) 。現在系統賦予了
你的高階聖堂武士一個能力,傳遞靈能,每次你可以選擇一個 i ∈ [2,n − 1],若
a i ≥ 0 則其兩旁的高階聖堂武士,也就是 i − 1、i + 1 這兩名高階聖堂武士會從
i 這名高階聖堂武士這裏各抽取 a i 點靈能;若 a i < 0 則其兩旁的高階聖堂武士,
也就是 i−1,i+1 這兩名高階聖堂武士會給 i 這名高階聖堂武士 −a i 點靈能。形
式化來講就是 a i−1 + = a i ,a i+1 + = a i ,a i − = 2a i 。
靈能是非常高效的作戰工具,同時也非常危險且不穩定,一位高階聖堂
武士擁有的靈能過多或者過少都不好,定義一組高階聖堂武士的不穩定度爲
max n
i=1 |a i |,請你通過不限次數的傳遞靈能操作使得你控制的這一組高階聖堂武
士的不穩定度最小。
【輸入格式】
本題包含多組詢問。輸入的第一行包含一個正整數 T 表示詢問組數。
接下來依次輸入每一組詢問。
每組詢問的第一行包含一個正整數 n,表示高階聖堂武士的數量。
接下來一行包含 n 個數 a 1 ,a 2 ,··· ,a n 。
試題 J: 靈能傳輸 15
第十屆藍橋杯大賽軟件類省賽C/C++大學B組
【輸出格式】
輸出 T 行。每行一個整數依次表示每組詢問的答案。
【樣例輸入】
3
3
5 -2 3
4
0 0 0 0
3
1 2 3
【樣例輸出】
3
0
3
【樣例說明】
對於第一組詢問:
對 2 號高階聖堂武士進行傳輸操作後 a 1 = 3,a 2 = 2,a 3 = 1。答案爲 3。
對於第二組詢問:
這一組高階聖堂武士擁有的靈能都正好可以讓他們達到最佳戰鬥狀態。
【樣例輸入】
3
4
-1 -2 -3 7
4
2 3 4 -8
5
-1 -1 6 -1 -1
【樣例輸出】
5
7
4
【樣例輸入】
見文件trans3.in。
【樣例輸出】
見文件trans3.ans。
【數據規模與約定】
對於所有評測用例,T ≤ 3,3 ≤ n ≤ 300000,|a i | ≤ 10 9 。
評測時將使用 25 個評測用例測試你的程序,每個評測用例的限制如下:
評測用例編號 n |a i | 特殊性質
1 = 3 ≤ 1000 無
2,3 ≤ 5 ≤ 1000 無
4,5,6,7 ≤ 10 ≤ 1000 無
8,9,10 ≤ 20 ≤ 1000 無
11 ≤ 100 ≤ 10 9 所有 a i 非負
12,13,14 ≤ 100 ≤ 10 9 無
15,16 ≤ 500 ≤ 10 9 無
17,18,19 ≤ 5000 ≤ 10 9 無
20 ≤ 5000 ≤ 10 9 所有 a i 非負
21 ≤ 100000 ≤ 10 9 所有 a i 非負
22,23 ≤ 100000 ≤ 10 9 無
24,25 ≤ 300000 ≤ 10 9 無
注意:本題輸入量較大請使用快速的讀入方式。

解題思路:

這題題量有點大。。。簡單來說就是給定一組數,我們的目標是通過兩種操作使得其中的絕對值最大的數達到最小,這兩種操作是:
1、如果 a[i] > 0 並且 a[i - 1] 或者 a[i+1] 小於 0,我們可以將 a[i] 借給 i-1 元素和 i+1 元素,同時 a[i] 要變成 -a[i]。
2、如果 a[i] < 0 並且 a[i - 1] 或者 a[i+1] 大於 0,我們可以將 a[i-1] 和 a[i+1] 各借 abs(a[i]) 給 i 元素,之後 i 元素的值變成 -a[i] 也就是正數(a[i] 本身是小於 0 的),同時 a[i - 1] 和 a[i + 1] 要減掉 abs(a[i])。
我們考慮幾種情況:
1、所有的數字都爲正數或者都爲負數,即所有的數字都同號。這種情況是沒法借的,因爲不符合操作要求,因此這種情況求出數組中絕對值最大的數即可。
2、對於 a[i],如果 a[i] 是正數,並且 a[i - 1] 和 a[i + 1] 至少有一個負數,那麼我們的目標就是把那個絕對值最大的負數的絕對值縮小,這個時候如果另一邊是正數,則需要考慮操作之後是否會產生新的絕對值更大的正數,比如現在有三個數:5 5 -6,如果我們把中間那個 5 按上面的操作 1 變換之後:10 -5 -1,絕對值最大數變成了 10,而之前是 6,顯然不行。如果這三個數是這樣的:1 5 -7,那麼我們就可以按操作 1 變換:6 -5 -2,最大絕對值從 7 減小到了 6,是可行的。那麼變換條件是什麼呢?這裏假設 a[i - 1] 是正數,a[i + 1] 是負數,那麼條件可以寫成:a[i - 1] + a[i] < abs(a[i + 1])。而如果 a[i - 1] 和 a[i + 1] 都是負數的時候,當兩邊的絕對值有一個大於 a[i] 時,就可以進行操作 1 變換。
3、對於 a[i] 是負數的時候,如果 a[i - 1] 是負數, a[i+1] 是正數,那麼確保 a[i + 1] > abs(a[i] + a[i-1]),就可以進行操作 2 變換,如果兩邊都是正數則只要有一邊的值大於 abs(a[i]) 時就可以進行操作 2 變換

代碼:

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;

const int MAXN = 300010;
int nums[MAXN]; 

// 判斷 a 和 b 是否異號 
bool judgeYi(int a, int b) {
	return a > 0 && b < 0 || a < 0 && b > 0;
} 

int main() {
	int T, n;
	cin >> T;
	while (T--) {
		cin >> n;
		// 標誌負數和正數是否出現 
		bool hasNe = false, hasPo = false;
		int res = 0;
		for (int i = 0; i < n; i++) {
			scanf("%d", &nums[i]);
			if (nums[i] < 0) {
				hasNe = true;
			} else if (nums[i] > 0) {
				hasPo = true;
			}
		}
		// 如果數組中同時存在正負數,則判斷能否進行操作 1 和 操作 2 
		if (hasNe && hasPo) {
			bool canNext;
			do {
				canNext = false;
				for (int i = 1; i < n - 1; i++) {
					// nums[i] 和 nums[i-1] 或 nums[i+1] 異號 
					if (judgeYi(nums[i], nums[i-1]) || judgeYi(nums[i], nums[i+1])) {
						if (nums[i] > 0) {
							// nums[i-1] 和 nums[i+1] 異號,
							// 這裏的 if 和 else 可以合併,爲了邏輯清晰,這裏分開寫 
							if (judgeYi(nums[i-1], nums[i+1])) {
								if ((nums[i-1] > 0 && abs(nums[i+1]) > nums[i-1] + nums[i]) || 
									(nums[i+1] > 0 && abs(nums[i-1]) > nums[i+1] + nums[i])) {
									nums[i+1] += nums[i];
									nums[i-1] += nums[i];
									nums[i] = -nums[i];
									canNext = true;
								}
							} else { // nums[i-1] 和 nums[i+1] 同號,都 < 0 
								if (abs(nums[i-1]) > nums[i] || abs(nums[i+1]) > nums[i]) {
									nums[i+1] += nums[i];
									nums[i-1] += nums[i];
									nums[i] = -nums[i];
									canNext = true;
								} 
							} 
						} else if (nums[i] < 0) {
							// nums[i-1] 和 nums[i+1] 異號 
							if (judgeYi(nums[i-1], nums[i+1])) {
								if ((nums[i-1] > 0 && nums[i-1] > abs(nums[i+1] + nums[i])) || 
									(nums[i+1] > 0 && nums[i+1] > abs(nums[i-1] + nums[i]))) {
									nums[i+1] += nums[i];
									nums[i-1] += nums[i];
									nums[i] = -nums[i];
									canNext = true;
								}
							} else { // nums[i-1] 和 nums[i+1] 同號,都 > 0 
								if (nums[i-1] > abs(nums[i]) || nums[i+1] > abs(nums[i])) {
									nums[i+1] += nums[i];
									nums[i-1] += nums[i];
									nums[i] = -nums[i];
									canNext = true;
								} 
							} 
						}
					} 
				}
			} while (canNext);
		}
		int t;
		// 求絕對值最大的值 
		for (int i = 0; i < n; i++) {
			res = max(res, abs(nums[i]));
		}
		cout << res << endl;
	}
	
	return 0;
}

藍橋杯,ACM算法進階資料大放送

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