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)把四個位置串起來,總共六個 if3.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\) 行答案自然連在一起,最後再換行收尾。

測過再交:範例把六個字元全蓋到了,好好利用

觀察四個範例的輸出:ACEFDEBD——六個字元全部出現過。這代表一件很好的事:你的六列對照表只要有任何一列抄錯,把四個範例全部跑一遍就一定會現形。例如把 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\))——不過六個值仍然互不相同,照樣分得開。兩種寫法都是滿分。

常犯錯誤
  1. 對照表抄錯一列:這題唯一的技術含量就在抄表——考場上直接對著題目的表逐列核對,別憑記憶。
  2. 想把一行讀成一個四位數:每行是 0 1 0 1——四個分開的數字,cin >> code 只會讀到第一個 \(0\) 就停,後面的數字全部錯位,範例 1 會什麼都印不出來。照主文用四個變數 >> 四次即可。
  3. 字元之間多印了空白或換行:每翻譯一個字元就 endl 的話,範例 3 會印成兩行的 EF——這跟 EF 是不同的答案,會 WA。(結尾多一個換行沒關係,字元之間的間隔才是真的會錯。)
  4. 忘記先讀 \(N\):第一行的 \(N\) 沒讀掉的話,它會被當成第一行編碼的第一個位元,後面全部錯位——範例 1 同樣什麼都印不出來。