NOIP 2018普及組複賽C++詳細題解報告(2)
第2題 龍虎鬥
一、分析
(1)ci最大值是10億,n最大值是10萬,相乘明顯會超過INT_MAX,所以本題要用long long才有可能得滿分。若用int,最多隻能得80分。
(2)若能想到c是count的縮寫,p是position的縮寫,s是soldier的縮寫,對理解題意會有所幫助。
(3)p1和s1是個很弱的干擾項,直接加到該位置即可。
二、演算法實現
求氣勢差的最小值,有兩種方法。
第一種方法是暴力列舉,每移動一次,就判斷氣勢差距是不是變得更小了。這種方法,最多可能要計算10萬次。通常幾百萬內的迴圈不會執行超時。
方法一 完整程式碼
#include <iostream> #include <cstdio> #include <cmath> using namespace std; #define LL long long int main() { freopen("fight.in","r",stdin); freopen("fight.out","w",stdout); int n, i, m, p1, p2; cin >> n; LL c[n + 1], s1, s2; for(i = 1; i <= n; i++) { cin >> c[i]; } cin >> m >> p1 >> s1 >> s2; c[p1] += s1; LL lPower = 0; // 龍勢力 for(i = 1; i < m; i++) { lPower += c[i] * (m - i); } LL rPower = 0; for(i = m + 1; i <= n; i++) { rPower += c[i] * (i - m); } //cout << lPower << ' ' << rPower << endl; LL gap = abs(lPower - rPower); // 雙方的氣勢差 p2 = m; //預設放在m位置 for(i = 1; i <= n; i++) { if(i < m)// 神兵加到左側龍勢力 { if(abs(lPower + s2 * (m - i) - rPower) < gap) { gap = abs(lPower + s2 * (m - i) - rPower); p2 = i; } } else if(i > m) // 神兵加到右側虎勢力 { if(abs(rPower + (i - m) * s2 - lPower) < gap) { gap = abs(rPower + (i - m) * s2 - lPower); p2 = i; } } } cout << p2 << endl; return 0; }
評測結果:

2.png
第二種方法,用龍勢力和虎勢力的氣勢差除去s2,會得到s2離m號兵營的距離dist。注意dist不能定義為整數,只能定義為浮點數。當龍勢力小於虎勢力且距離中包含0.5時,要往左多挪一個位置,這樣得到的p2與m的距離最大,從而p2取得最小值 ;當龍勢力大於虎勢力且距離中包含0.5時,0.5直接去掉,這樣就不用往右再挪一個距離了。這樣得到的p2與m的距離最小,從而p2取得最小值。
舉兩個例子,這兩個例子裡不用考慮p1和s1,對理解題意影響不大。
例1
n = 3,c1 = 10, c3 = 11,c2等於隨便一個數,比如1。m = 2。s2 = 2。
在s2沒放進去時,勢力差gap = 虎勢力 - 龍勢力 = 1。dist = gap / s2 = 0.5,所以p2 = m – int(dist + 0.5) = 1,即要把s2放到1號營裡。
此時龍勢力 = 10 + 2 = 12, 虎勢力 = 11,gap仍然是1,但此時的p2最小。
例2
n = 3,c1 = 11, c3 = 10,c2等於隨便一個數,比如1。m = 2。s2 = 2。
在s2沒放進去時,勢力差gap = 龍勢力 – 虎勢力 = 1。dist = gap / s2 = 0.5,所以p2 = m + ceil(dist -0.5) = m + 0 = m,即要把s2放到中立的m號營裡。
此時龍勢力 = 11, 虎勢力 = 10,m號營勢力 = 1 + 2 = 3,gap仍然是1,此時的p2最小。
方法二完整程式碼
#include <iostream> #include <cmath> #include<cstdio> using namespace std; #define LL long long int main() { freopen("fight21.in","r",stdin); freopen("fight.out","w",stdout); int n, i, m, p1, p2; LL s1, s2; cin >> n; LL c[n + 1]; for(i = 1; i <= n; i++) { cin >> c[i]; } cin >> m >> p1 >> s1 >> s2; c[p1] += s1; LL lPower = 0; // 龍勢力 for(i = 1; i < m; i++) { lPower += c[i] * (m - i); } LL rPower = 0; for(i = m + 1; i <= n; i++) { rPower += c[i] * (i - m); } //cout << lPower << ' ' << rPower << endl; LL gap = abs(lPower - rPower); // 雙方的氣勢差 p2 = m;// 暫放在m的位置上 float dist = gap * 1.0 / s2; // s2離m兵營的距離 if(rPower > lPower)// 虎勢力大,s2要放在龍方 { //這裡floor()也可以改為int() p2 = m - floor(dist + 0.5); if(p2 < 1) { p2 = 1; // 不能越界 } } else if(rPower < lPower) { // 注意,這裡0.5是要捨棄掉的,大於0.5才加1 p2 = m + ceil(dist - 0.5); if(p2 > n) { p2 = n; // 不能越界 } } cout << p2 << endl; return 0; }
少兒程式設計諮詢、演算法諮詢請加微信307591841或QQ群581357582

諾依曼演算法公眾號.jpg