Editorial for 特殊編碼 (APCS 2018-06 初級)
簡潔題意
A 到 F 六個字元各有一個長度 \(4\) 的二元編碼(對照表在題目裡)。讀入 \(N\)(\(1 \le N \le 4\))行、每行四個以空白隔開的 \(0\)/\(1\),把每行翻譯回字元、連著印出(字元之間不能有空白或換行)。
依正確通過的測試資料筆數給分,其中:
- 子題組 1(\(50\) 分):\(N = 1\)。
- 子題組 2(\(50\) 分):\(N \le 4\)。
照著對照表逐列翻譯
這一題不需要任何演算法——對照表題目整張給你了,要做的就是把表抄成程式。
先安排讀入。每行長得像 0 1 0 1:四個數字、彼此以空白隔開,所以用四個 int 變數接、>> 四次就好(cin >> 讀數字時會自動跳過空白和換行)。接著照對照表逐列比對:這一行的四個數字吻合哪一列,就印出哪個字元——每列翻成一個 if,條件用「且」(3.3)把四個位置串起來,總共六個 if(3.5)。題目保證每行必定是六種編碼之一,所以不用擔心「都沒吻合」的情況。
最外面包一層 for 迴圈(4.3)跑 \(N\) 行。順帶一提:子題組 1 保證 \(N = 1\),連迴圈都不用寫就有 \(50\) 分;包上 for 就是滿分——這題的滿分和 \(50\) 分只差一個迴圈。完整程式:
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int a, b, c, d;
cin >> a >> b >> c >> d; // 一行四個數字,>> 會自動跳過空白
// 照對照表逐列翻譯:吻合哪一列就印哪個字元
if (a == 0 && b == 1 && c == 0 && d == 1) cout << "A";
if (a == 0 && b == 1 && c == 1 && d == 1) cout << "B";
if (a == 0 && b == 0 && c == 1 && d == 0) cout << "C";
if (a == 1 && b == 1 && c == 0 && d == 1) cout << "D";
if (a == 1 && b == 0 && c == 0 && d == 0) cout << "E";
if (a == 1 && b == 1 && c == 0 && d == 0) cout << "F";
}
cout << endl;
return 0;
}
印的時候直接接在前一個字元後面(不換行、不加空白),跑完 \(N\) 行答案自然連在一起,最後再換行收尾。
測過再交:範例把六個字元全蓋到了,好好利用
觀察四個範例的輸出:A、C、EF、DEBD——六個字元全部出現過。這代表一件很好的事:你的六列對照表只要有任何一列抄錯,把四個範例全部跑一遍就一定會現形。例如把 D 那列抄錯的話,範例 4 會印出 EB——兩個 D 憑空消失,一眼就看得出來。
所以這題的自測就是「把表驗一遍」:範例全跑之外,也可以自己把六種編碼一口氣餵下去對答案:
| 餵什麼 | 正確輸出 | 這一筆在測什麼 |
|---|---|---|
| \(N = 4\),四行依序放 A、B、C、D 的編碼 | ABCD |
對照表前四列一口氣驗完 |
| \(N = 2\),兩行依序放 E、F 的編碼 | EF |
剩下兩列(恰好就是範例 3) |
兩筆都親眼看過正確,這題就穩了。
更簡潔的寫法:把四個數字排成一個四位數
六個 if 每個都要寫四項比較,有點囉唆。有個完全不需要新語法的巧思:把四個數字照原樣排成一個四位數。1 1 0 1 排起來就是 \(1101\),而「把四個一位數組成一個數」正是 2.6 拆位套路的反向操作——拆位用 % 10 和 / 10 一位一位剝下來,組回去就是每一位乘上自己的位值(千、百、十、個)再相加:
int num = a * 1000 + b * 100 + c * 10 + d; // 四個數字照原樣排成一個四位數
if (num == 101) cout << "A"; // 0101 → 101(開頭的 0 會消失)
else if (num == 111) cout << "B"; // 0111 → 111
else if (num == 10) cout << "C"; // 0010 → 10
else if (num == 1101) cout << "D";
else if (num == 1000) cout << "E";
else if (num == 1100) cout << "F";
每列的比較從四項縮成一項,數值跟編碼長得一模一樣、不用任何換算,其餘部分完全不變。唯一要留意的是開頭的 \(0\) 會消失(\(0101\) 併出來是 \(101\)、\(0010\) 是 \(10\))——不過六個值仍然互不相同,照樣分得開。兩種寫法都是滿分。
常犯錯誤
- 對照表抄錯一列:這題唯一的技術含量就在抄表——考場上直接對著題目的表逐列核對,別憑記憶。
- 想把一行讀成一個四位數:每行是
0 1 0 1——四個分開的數字,cin >> code只會讀到第一個 \(0\) 就停,後面的數字全部錯位,範例 1 會什麼都印不出來。照主文用四個變數>>四次即可。 - 字元之間多印了空白或換行:每翻譯一個字元就
endl的話,範例 3 會印成兩行的E、F——這跟EF是不同的答案,會 WA。(結尾多一個換行沒關係,字元之間的間隔才是真的會錯。) - 忘記先讀 \(N\):第一行的 \(N\) 沒讀掉的話,它會被當成第一行編碼的第一個位元,後面全部錯位——範例 1 同樣什麼都印不出來。