Editorial for 程式考試 (APCS 2023-01 初級)
簡潔題意
\(K\) 筆提交紀錄,第 \(i\) 筆有時間 \(t_i\) 與分數 \(s_i\),\(s_i = -1\) 代表嚴重錯誤。總分 \(=\) 最高分 \(-\) 總提交次數 \(-\) 嚴重錯誤次數 \(\times 2\),算出來是負的就以 \(0\) 計。輸出總分,以及第一次拿到最高分的時間(\(1 \le K \le 6\)、\(-1 \le s_i \le 100\);時間嚴格遞增,第一筆保證不是嚴重錯誤)。
依正確通過的測試資料筆數給分,其中:
- 子題組 1(\(60\) 分):\(K = 3\)。
- 子題組 2(\(40\) 分):無額外限制。
先拿下子題組 1(\(60\) 分):三筆紀錄,if 就寫得完
\(K = 3\) 時把三筆紀錄讀成六個變數就好,連迴圈都不用——只用到語法書上冊第三單元為止的語法。
找最高分用擂台:題目保證第一筆不是嚴重錯誤,就讓第一筆直接站上擂台(best = s1、bestTime = t1),後兩筆輪流上來挑戰。這裡有一個全題最關鍵的字:挑戰條件用 >,不能用 >=。題目要的是「第一次拿到最高分的時間」——同分的挑戰者不換人,擂台上就永遠站著最早的那一次;寫成 >= 就變成記到最後一次了。
#include <iostream>
using namespace std;
int main() {
int k; // 子題組 1 保證 k = 3
int t1, s1, t2, s2, t3, s3;
cin >> k;
cin >> t1 >> s1 >> t2 >> s2 >> t3 >> s3;
int best = s1, bestTime = t1; // 第一筆保證不是嚴重錯誤,直接站上擂台
if (s2 > best) { // 用 > 不用 >=:同分時保留較早那次
best = s2;
bestTime = t2;
}
if (s3 > best) {
best = s3;
bestTime = t3;
}
int errors = 0; // 嚴重錯誤次數
if (s2 == -1) errors += 1;
if (s3 == -1) errors += 1;
int score = best - 3 - errors * 2;
if (score < 0) score = 0; // 負分以 0 計
cout << score << " " << bestTime << endl;
return 0;
}
範例 1 的 \(K = 5\),這支程式讀不完,答案會是錯的——這是正常的:APCS 逐筆給分,這一版穩穩拿下子題組 1 的 \(60\) 分(範例 2 剛好是 \(K = 3\),會過)。
從 \(60\) 分到 \(100\) 分:把六個變數換成一個迴圈
\(K\) 筆紀錄要做的事完全一樣:上擂台比一次、是 \(-1\) 就把嚴重錯誤加一。改用第四單元的單層 for,每輪讀一筆、當場處理掉,連存都不用存——不需要陣列。
改用迴圈之後,「讓第一筆先站上擂台」改用初值處理:分數最低是 \(-1\),把 best 的初值設成值域外的 \(-2\),第一筆不管是幾分都一定打得贏它。(要不要擔心 \(-1\) 反過來霸佔擂台?不用——題目保證第一筆不是嚴重錯誤,第一輪過後擂台上就站著一個 \(\ge 0\) 的分數,\(-1\) 永遠打不贏。)
#include <iostream>
using namespace std;
int main() {
int k;
cin >> k;
int best = -2, bestTime = 0; // 分數最低是 -1,-2 一定會被第一筆換掉
int errors = 0;
for (int i = 1; i <= k; i++) {
int t, s;
cin >> t >> s;
if (s > best) { // 用 > 不用 >=:同分時保留較早那次
best = s;
bestTime = t;
}
if (s == -1) {
errors += 1;
}
}
int score = best - k - errors * 2;
if (score < 0) score = 0; // 負分以 0 計
cout << score << " " << bestTime << endl;
return 0;
}
測過再交:範例沒考 \(K = 1\) 和「全部同分」
範例 1 考了「最高分出現兩次」(時間要取 \(10\) 不是 \(20\))、範例 2 考了負分歸零。還缺這幾種:
| 輸入 | 正確輸出 | 在測什麼 |
|---|---|---|
1 / 7 100 |
99 7 |
\(K = 1\):只有一筆,也要扣那一次提交 |
4 / 1 50、2 50、3 50、4 50 |
46 1 |
全部同分:時間要取最早的第 \(1\) 筆 |
6 / 1 8、2 -1、3 5、4 3、5 2、6 1 |
0 1 |
\(8 - 6 - 2\) 剛好等於 \(0\):不是負的,不該被動到 |
三個都親眼看過正確,這題就穩了。
常犯錯誤
- 擂台寫成
>=:同分時換成最後一次,「第一次拿到最高分」就錯了。範例 1 當場現形(印83 20,正確是83 10)。 - 忘了負分歸零:範例 2 當場現形(印
-7 3,正確是0 3)。 - 嚴重錯誤只扣一倍:公式是扣 \(2\) 分一次。範例 1 當場現形(印
84 10)。 best初值設 \(0\):\(0\) 是合法的分數——最高分就是 \(0\) 時,擂台從頭到尾沒人上去過,bestTime停在沒意義的初值。範例 2 當場現形(印0 0,正確是0 3)。