---
# System prepended metadata

title: 【Uva 題庫解題】CPE 二顆星選集 - part1
tags: [資料結構, 程式設計, C++, 競程, 演算法, CP]

---

# 【Uva 題庫解題】CPE 二顆星選集 - part1

[TOC]

本筆記及該系列主要以每篇 5 題為主，也僅供筆者個人學習用途，斟酌參考。

若你喜歡本筆記，不妨為文章按下一顆愛心，或是追蹤我的個人公開頁，感謝您點入本篇筆記。

CPE 二顆星選集來源：https://par.cse.nsysu.edu.tw/~advprog/star.php

## Uva 151 - Power Crisis

PDF Source：https://onlinejudge.org/external/1/151.pdf

Uva Online Judge：https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=87

Zerojudge：https://zerojudge.tw/ShowProblem?problemid=q891

該題為約瑟夫問題的變體。

題目架構：

- 共 $N$ 個地區（ $1$ 到 $N$ ）
- 地區 $1$ 總是第一個被切斷。
- 剩下 $N-1$ 個地區，從地區 $2$ 開始，每數到第 $m$ 個地區就切斷它。
- 目標：找出最小的 $m$，使得地區 13 是最後一個被切斷的。

轉化成約瑟夫問題的形式：

首先由於地區 $1$ 都是第一個被切斷的，只需考慮 $N - 1$ 的地區即可，因此就變成長度為 $N - 1$ 的約瑟夫問題。

另外為方便計算，將地區給重新編號：

- 地區 $2 \rightarrow 0$
- 地區 $3 \rightarrow 1$
- $\cdots$
- 地區 $13 \rightarrow 11$

這樣就需要找到一個 $m$，使得在這 $N-1$ 個人的環中，最後被切斷的索引為 11。

約瑟夫問題公式：$$f(n, m) =
\begin{cases}
0 & \text{if } n = 1 \\
(f(n-1, m) + m) \% n & \text{if } n > 1
\end{cases}$$

範例程式碼：

```cpp=
#include <iostream>

using namespace std;

int ans(int n, int k){
    int s = 0;
    for (int i = 2; i <= n; ++i){
        s = (s + k) % i;
    }
    return s;
}

int main(){
    ios::sync_with_stdio(false), cin.tie(NULL);
    int N;
    while (cin >> N && N != 0){
        int m = 1;
        while (true){
            int last = ans(N - 1, m);
            if (last == 11){
                cout << m << '\n';
                break;
            }
            m++;
        }
    }
}
```

## Uva 245 - Uncompress

PDF Source：https://onlinejudge.org/external/2/245.pdf

Uva Online Judge：https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=181

Zerojudge：https://zerojudge.tw/ShowProblem?problemid=e569

題目翻譯：

有一種用來建立文檔壓縮版本的簡單方法，可用於不包含任何數字字元的檔案。此壓縮方法需要先建立一份「未壓縮檔中出現過的單字」清單。

當在未壓縮檔中遇到非字母字元時，就直接將該字元複製到壓縮檔中。

當在未壓縮檔中遇到一個單字時，只有在該單字第一次出現時，才會將它直接複製到壓縮檔；並在此情況下，把該單字放到清單的最前端。

若不是第一次出現，則不把該單字複製到壓縮檔；改為把它在清單中的位置（編號）寫入壓縮檔，並將該單字移到清單最前端。清單位置的編號從 1 開始。

請撰寫一個程式，輸入為一個壓縮檔，輸出為原本未壓縮檔的還原結果。

解題思路：

基本上就是按照題目要求做事。

1. 遇到單字時：直接印出該單字，把這個單字放到清單的最上面，原本清單裡的單字全部往後擠一位。
2. 遇到數字時（如 3）：數字 3 表示到清單裡面去找第 3 個單字，接著把那單字印出來，最重要的是，用完之後要把單字移到清單最上面去。
3. 遇到標點符號或空格時：直接印出即可。

輸入方式可用 `cin.get(ch)`，其他方式有可能會將標點符號或空格給讀掉（例如 `sstream`）。

透過 `isalpha()` 判斷字元是否為字母，以及 `isdigit()` 判斷字元是否為數字。

演算法流程（供參）：

1. 建立一個字串 `buffer` 以及單字清單 `wlist`。
2. 用 `cin.get(ch)` 一個一個讀入字元。
3. 判斷為字母 -> `buffer += ch`。
4. 判斷為數字 -> `buffer += ch`。
5. 以上皆非，則先判斷 `buffer` 是否為空。
6. `buffer` 非空 -> 表示裡面有單字或是數字之類的。
    - 透過 `isalpha()` 判斷 `buffer` 開頭是否為字母，得知是否為單字。是的話直接印出，並 `wlist.insert(wlist.begin(), buffer)` 把單字放到最前面去。
    - 若不是字母，就表示他是一個數字，可用 `stoi(buffer)` 轉整數，判斷是否為 0，是的話就結束，否則繼續。
    - 若要繼續，則印出 `wlist[數字 - 1]` 這個單字，接著把它刪了。 `wlist.erase(wlist.begin() + (數字 - 1))` ，然後再把單字插入到最前面。
7. 上述流程跑完後清空 `buffer`。
8. 若上述沒有一個條件成立，就表示是標點符號或空格，直接輸出即可：`cout << ch`。

範例程式碼：

```cpp=
#include <bits/stdc++.h>

using namespace std;

int main(){
    ios::sync_with_stdio(false), cin.tie(nullptr);
    
    vector <string> wlist;
    
    char ch;
    string buffer = "";
    
    while (cin.get(ch)){
        if (isalpha(ch)){
            buffer += ch;
        }
        else if (isdigit(ch)){
            buffer += ch;
        }
        else{
            if (!buffer.empty()){
                if (isalpha(buffer[0])){
                    cout << buffer;
                    wlist.insert(wlist.begin(), buffer);
                }
                else{
                    int index = stoi(buffer);
                    
                    if (index == 0) return 0;
                    
                    string s = wlist[index - 1];
                    
                    cout << s;
                    
                    wlist.erase(wlist.begin() + (index - 1));
                    wlist.insert(wlist.begin(), s);
                }
                buffer = "";
            }
            cout << ch;
        }
    }
}
```

## Uva 255 - Correct Move

PDF Source：https://onlinejudge.org/external/2/255.pdf

Uva Online Judge：https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=191

Zerojudge：https://zerojudge.tw/ShowProblem?problemid=e601

題目翻譯：

我們有一個 64 個格子的正方形棋盤，從 0 到 63 編號，如圖 1 所示。

有兩個棋子：國王和皇后。國王所在格子與皇后所在格子這一對位置稱之為「國家」（state）。

- 如果兩個棋子不在同一個格子上，則該狀態是合法的（legal）。
- 國王與皇后輪流移動。
- 國王可以在水平方向或垂直方向走一步，只要它不會走到皇后所在的位置。
- 皇后可以在水平方向或垂直方向走一步或多步，只要它在移動途中不會碰到國王。
- 所有這些移動都稱為合法移動（legal）。
- 請注意，棋子不能沿對角線移動。

![image](https://hackmd.io/_uploads/r1_neOEIbl.png)

圖 1。

例如，假設我們有一個狀態：國王在第 17 格、皇后在第 49 格，如圖 2 所示。

國王的合法走法可以到第 9、16、18、25 格；而皇后合法地可以走到第 25、33、41、48、50、51、52、53、54、55 或 57 格。

不過，我們另外加上一個額外限制：棋子不可以移動到「另一個棋子也能移動到」的位置。

滿足這個限制的合法走法稱為「允許」（allowed）的走法。

在圖 2 中，國王與皇后各自透過一個允許走法所能到達的所有位置，分別用空心圓（○）與實心點（●）表示。

在圖 3 中，國王不能移動，它被困住了（locked in）。

![image](https://hackmd.io/_uploads/HkjeZdEUbe.png)

圖 2。

![image](https://hackmd.io/_uploads/HyVfWONU-l.png)

圖 3。

此問題要你寫一個程式，對女王的移動進行一些檢查。

解題思路：

一個 $8 \times 8$ 棋盤，可以用以下的方法來做表示：

- `r = pos / 8`
- `c = pos % 8`

接著就是去設計每一個條件的程式：

1. Illegal state：當 `k == q`，國王跟皇后在同一格，表示狀態不合法。
2. Illegal move：皇后走法不合法。
    - `q == q2` 皇后都沒動，違反「至少動一步」的規則。
    - 判斷是否同列（`qr == nr`）、同行（`qc == nc`），若皆非表示是斜著走，因此也不合法。
    - 若同列或同行，沿移動方向（如 `step = (nr > qr ? 1 : -1)`）一格一格掃過路徑（從下一格開始，含終點），若掃到 `k` 表示皇后會碰到國王，因此也不合法。
3. Move not allowed：不能走到國王能走的格子。
    - 若皇后新位置 q2 恰好是國王從 k 出發「上下左右一步」能到的格（即曼哈頓距離為 1），則輸出 Move not allowed。
4. Continue / Stop：當皇后成功走到 q2（而且是 allowed）後，輪到國王行動，此時要判斷國王是否至少有一個 allowed 的一步。
    - 國王能走的只有四步 `(-1,0), (1,0), (0,-1), (0,1)`，因此窮舉這幾步。（`r = kr + d[0], c = kc + d[1]`）
    - 建立 `k2 = r*8 + c` 表示新狀態的國王。
        1. 檢查 k2 是否出界。
        2. 檢查是否 `k2 == q2`
        3. 檢查 `k2` 是否為皇后在 `q2` 的位置可以走到的格（用 `queen_attacks(q2, k, k2)` 判斷）
            - 皇后能攻擊/到達的格：同列或同行，且從 `q2` 走到該格的路徑上不能先撞到國王（國王會擋住皇后的直線）。
        5. 若上述條件皆成立，表示 `Stop` 國王被卡死了，否則 `Continue` 國王可繼續走。

範例程式碼：

```cpp=
#include <bits/stdc++.h>

using namespace std;


bool queen_attacks(int qpos, int kpos, int target){
    if (target == qpos) return false;
    
    int qr = qpos / 8, qc = qpos % 8;
    int tr = target / 8, tc = target % 8;
    
     if (qr == tr){
        int step = (tc > qc ? 1 : -1);
        for (int c = qc + step; c != tc + step; c += step){
            int cur = qr * 8 + c;
            if (cur == kpos) return false;
            if (cur == target) return true;
        }
    } else if (qc == tc){
        int step = (tr > qr ? 1 : -1);
        for (int r = qr + step; r != tr + step; r += step){
            int cur = r * 8 + qc;
            if (cur == kpos) return false;
            if (cur == target) return true;
        }
    }
    return false;
}

int main(){
    ios::sync_with_stdio(false), cin.tie(nullptr);
    
    int k, q, q2;
    while (cin >> k >> q >> q2){
        if (k == q){
            cout << "Illegal state\n";
            continue;
        }
        
        int kr = k / 8, kc = k % 8;
        int qr = q / 8, qc = q % 8;
        int nr = q2 / 8, nc = q2 % 8;
        
        if (q == q2){
            cout << "Illegal move\n";
            continue;
        }
        
        bool illegal = false;
        
        if (qr == nr){
            int step = (nc > qc ? 1 : -1);
            for (int c = qc + step; c != nc + step; c += step){
                if (qr == kr && c == kc) illegal = true;
            }
        }
        else if (qc == nc){
            int step = (nr > qr ? 1 : -1);
            for (int r = qr + step; r != nr + step; r += step)
                if (qc == kc && r == kr) illegal = true;
        }
        else{
            illegal = true;
        }
        if (illegal){
            cout << "Illegal move\n";
            continue;
        }
        
        if (abs(kr - nr) + abs(kc - nc) == 1){
            cout << "Move not allowed\n";
            continue;
        }
        
        bool allowed = false;
        int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        for (auto &d : dir){
            int r = kr + d[0], c = kc + d[1];
            
            if (r < 0 || r >= 8 || c < 0 || c >= 8) continue;
            
            int k2 = r*8 + c;
            
            if (k2 == q2) continue;
            if (queen_attacks(q2, k, k2)) continue;
            
            allowed = true;
            break;
        }
        
        cout << (allowed ? "Continue\n" : "Stop\n");
    }
}
```

## Uva 294 - Divisors

PDF Source：https://onlinejudge.org/external/2/294.pdf

Uva Online Judge：https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=230

Zerojudge：https://zerojudge.tw/ShowProblem?problemid=d366

題目翻譯：

數學家喜歡研究各種數字的奇特性質。

例如，他們認為 945 是個有趣的數，因為它是第一個其因數總和大於它本身的奇數。

為了幫助他們尋找有趣的數字，你要撰寫一個程式，掃描一段數字範圍，並找出在該範圍內擁有最多因數的數字。

不幸的是，數字本身的大小以及範圍的大小，使得過於簡單的做法可能需要花太多時間才能跑完。

因此，請確保你的演算法足夠聰明，能在短短幾秒內處理最大的可能範圍。

筆者註：Divisors 稱為除數，也稱為我們最熟悉的因數。

解題思路：

要最快計算一個數字 $n$ 的因數個數，最快的方法是使用「質因數分解」（Prime Factorization）。

假設 $n$ 的標準分解式為：$$n = p_1^{a_1} \times p_2^{a_2} \times \dots \times p_k^{a_k}$$

則 $n$ 的因數個數 $d(n)$ 為：$$d(n) = (a_1 + 1) \times (a_2 + 1) \times \dots \times (a_k + 1)$$

個數就是拿原本質因數的指數來做計算，至於 + 1 部分，則是表示指數的選法（0 ~ $a_k$）。

解題步驟：

1. 建立質數表（用埃拉托斯特尼篩法, Sieve of Eratosthenes）：因為 $U$ 最大為 $10^9$，只需測到 $\sqrt{10^9} \approx 31622$ 的質數即可，先預先篩選出 $1$ 到 $32000$ 之間的所有質數。
2. 區間掃描：對每組測資 $L$ 和 $U$，遍歷 $i$ 從 $L$ 到 $U$。
3. 計算因數個數：對區間內的每個數字 $i$：
    - 用預存的質數表進行試除。
    - 統計每個質因數的冪次（exponent）。
    - 套用公式計算總因數個數。
    - 若試除完所有 $\le \sqrt{i}$ 的質數後，$i$ 仍然大於 1，代表剩下的 $i$ 本身就是一個大質數，因數個數需再乘以 $(1+1)=2$。
4. 找出最大值：若因數個數相同，題目要求輸出較小的那個數。

範例程式碼：

質數篩法優化部分：

```cpp=
for (int i = p * p; i <= MAXSQRT; i += p)
    is_prime[i] = false;
```

這段優化目的在於把目前質數 $p$ 的所有倍數都標記為非質數（false）。

`i += p` （$p$ 的倍數）即當找到質數 $p$ 後，刪掉所有$p$ 的倍數：$2p, 3p, 4p, \dots$ 等等。

這樣做的想法是如果 $i$ 是 $p$ 的倍數（且 $i > p$），那 $i$ 肯定不是質數，因為能被 $p$ 整除。

`int i = p * p`：在上層迴圈就將比 $p^2$ 小的倍數都篩選過了，因此到這邊從 $p^2$ 開始是合理的，不用從頭到尾遍歷一次，縮短耗時。

```cpp=
#include <bits/stdc++.h>

using namespace std;

const int MAXSQRT = 32000;

vector <int> prime;

// 埃拉托斯特尼篩法
void sieve(){
    vector <bool> is_prime(MAXSQRT + 1, true);
    
    is_prime[0] = is_prime[1] = false;
    
    for (int p = 2; p * p <= MAXSQRT; ++p){
        if (is_prime[p]){
            for (int i = p * p; i <= MAXSQRT; i += p)
                is_prime[i] = false;
        }
    }
    
    for (int p = 2; p <= MAXSQRT; ++p)
        if (is_prime[p])
            prime.push_back(p);
}

// 數有幾個因數
int countDivisor(int n){
    int total = 1;
    
    for (int p : prime){
        // 若質數平方大於 n 就不用再除
        if (p * p > n) break;
        
        if (n % p == 0){
            int count = 0;
            while (n % p == 0){
                count ++;
                n /= p;
            }
            // 當 n 無法被 p 整除時
            // count 即為 p 的指數
            total *= (count + 1);
        }
    }
    
    if (n > 1) total *= 2;
    
    return total;
}

int main(){
    ios::sync_with_stdio(false), cin.tie(nullptr);
    
    sieve();
    
    int T;
    cin >> T;
    while (T--){
        int L, U;
        cin >> L >> U;
        
        int max_num = -1;
        int max_num_count = -1;
        
        for (int i = L; i <= U; ++i){
            int d = countDivisor(i);
            
            if (d > max_num_count){
                max_num_count = d;
                max_num = i;
            }
        }
        
        cout << "Between " << L << " and " << U << ", " << max_num << " has a maximum of " << max_num_count << " divisors.\n";
    }
}
```

## Uva 337 - Interpreting Control Sequences

PDF Source：https://onlinejudge.org/external/3/337.pdf

Uva Online Judge：https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=273

No Zerojudge

題目翻譯：

幾乎所有的文字模式終端（text-mode terminal）都是專用電腦系統，包含一個序列埠（serial port，用於與數據機或其他電腦系統通訊）、鍵盤、陰極射線管顯示器（CRT，cathode-ray tube），當然還有一顆微處理器（microprocessor）、一些隨機存取記憶體（RAM）與存放控制程式的唯讀記憶體（ROM）。

當一個字元從鍵盤或序列埠送到終端時，終端的軟體會將它分類為要顯示在 CRT 上的顯示字元（display character），或是引入控制序列（control sequence）的字元。控制序列用來指示終端執行特殊動作，例如清除螢幕、以指定方式移動游標，或改變字型等。

在此題中，假設你正在為一個小型終端撰寫軟體，該終端有一個 10 行 × 10 列的顯示（例如用於銷售點終端，point-of-sale terminal）。行（row）與列（column）編號皆為 0 到 9。引入控制序列的字元為「∧」（circumflex，插入符號）。緊接在該控制序列引導字元之後的一個字元（或在一種情況下的兩個字元）會指示你的軟體執行特殊功能。以下為你需要解讀的完整控制序列清單：

∧b
將游標移到當前行的開頭；游標的行（row）不變。

∧c
清除整個螢幕；游標的行與列皆不變。

∧d
若可能，將游標下移一行；游標的列（column）不變。

∧e
擦除游標所在行中游標列及其右側的字元；游標的行與列皆不變。

∧h
將游標移到第 0 行、第 0 列；螢幕內容不改變。

∧i
進入插入模式（insert mode，見下）。

∧l
若可能，將游標左移一列；游標的行不變。

∧o
進入覆寫模式（overwrite mode，見下）。

∧r
若可能，將游標右移一列；游標的行不變。

∧u
若可能，將游標上移一行；游標的列不變。

∧∧
在目前游標位置寫出一個脫字符「∧」，其行為與該字元並非特殊字元時相同；此寫入仍受目前模式（插入或覆寫）的影響。

∧##
將游標移到所指定的行與列；# 代表一個十進位數字；第一個 # 表示新的行號，第二個 # 表示新的列號。

系統保證不會送來非法的控制序列。游標不得移出允許的螢幕位置範圍（亦即介於第 0 行第 0 列到第 9 行第 9 列之間）。

當一個一般字元（非控制序列的一部分）送到終端時，其在螢幕上的顯示方式取決於終端的模式。

當終端處於覆寫模式（開機時即為覆寫模式）時，接收到的字元會取代游標位置處的字元。

若處於插入模式時，游標位置及其右方的字元會整體向右移一列，然後將新字元放在游標位置；游標所在行中最右邊的字元會因此遺失。

無論是哪一種模式，游標都會在可能的情況下向右移一列。

**Input**

輸入資料將包含多組對你終端軟體的測試。

每一組測試以一行整數 N 開始，接下來會有 N 行資料；這些資料中的每一個字元，都必須依照讀取的順序，視同輸入到你的終端軟體中來處理。

輸入資料中不會包含定位字元（tab），而輸入中的換行符號必須被忽略。

請注意，輸入資料中的空白字元（blank）是一般字元，必須顯示在終端螢幕上。

最後一組測試之後，會接著出現一行只包含整數 0 的資料，表示輸入結束。

任何控制序列都不會被拆分到兩行輸入資料之中。

在每一個測資開始時，請假設終端螢幕是清空的（也就是整個畫面都填滿空白字元）、終端處於覆寫模式（overwrite mode），且游標位於螢幕的第 0 行、第 0 列。

**Output**

對於每一個輸入測資，請輸出一行，包含該測資的編號（自 1 起依序編號），以及在完成該測資所有資料處理後，螢幕最終呈現的畫面內容。請將螢幕畫面以一個「方框」包圍；所需的輸出格式請參考下方範例說明。

**Sample Input**

```
7
This is bad^h^c
^05^^
^14/ \^d^b / \
^u^d^d^l^l^l^l^l^l^l^l^l
^r^r< ACM >^l^l^d/^b \
^b^d \ /
^d^l^lv
7
^i9^l8^l7^l6^l5^l4^l3^l2^l1^l0
^o^d^lThis is #1^d^bThis is #2
^d^bThis is #3^d^bThis is #4
^d^bThis is #5^d^bThis is #6
^d^bThis is #7^d^bThis is #8
^i^d^bThis is #9^d^bThis is #10
^54^e Hello^d^l^l^l^lWorld
0
```

**Sample Output**

```
Case 1
+----------+
|     ^    |
|    / \   |
|   /   \  |
|  < ACM > |
|   \   /  |
|    \ /   |
|     v    |
|          |
|          |
|          |
+----------+
Case 2
+----------+
|0123456789|
|This is #1|
|This is #2|
|This is #3|
|This is #4|
|This Hello|
|This World|
|This is #7|
|This is #8|
|This is #0|
+----------+
```

解題思路：

這題其實沒有很難，就是題目長了點，基本上都按照題目的去做即可。

在思路上，可以先設定一個 `vector <string> scr(10, string(10, ' '))` ，表示一個 $10 \times 10$ 的螢幕畫面。

接著 `int r = 0, c = 0` 表示目前的游標（cursor）位置。

可以建立兩個函數：`putChar(ch)`、`clearScreen()`，把程式碼包裝起來，後續就不用全擠在一個 for 迴圈裡面。

範例程式碼：

```cpp=
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    
    int N, test_case = 1;
    while (cin >> N && N != 0) {
        cin.ignore();
        
        vector<string> scr(10, string(10, ' '));
        int r = 0, c = 0;
        bool insertMode = false;
        
        // [&](char ch) 匿名函數
        // putChar 本身就會是一個函數
        // clearScreen 也一樣
        // [&] 即為捕捉到的變數以 reference 的方式傳遞
        auto putChar = [&](char ch){
            if (!insertMode){
                scr[r][c] = ch;
            }
            else{
                // 進入插入模式
                // 在位置 (r, c) 做插入的話
                // 其位置後續字元都要往後挪移
                // 因而用 for(int j = 9; j > c; --j)
                // e.g. : abcdefg, 插入 x -> abcdxefg
                // x 插入在其中, efg 要往後挪移
                for (int j = 9; j > c; --j) scr[r][j] = scr[r][j - 1];
                scr[r][c] = ch;
            }
            if (c < 9) c++; // 邊界檢查
        };
        
        auto clearScreen = [&]() {
            // assign 為 string 標頭檔的方法
            // row.assign(10, ' ')
            // 即在字串 row 中, 用長度 10 的 ' ' 空白字元
            // 覆蓋掉原本的內容
            for (auto &row : scr) row.assign(10, ' ');
        };
        
        for (int k = 0; k < N; ++k){
            string line;
            getline(cin, line);
            
            for (int i = 0; i < line.size(); ++i){
                char ch = line[i];
                
                if (ch != '^'){
                    putChar(ch);
                    continue;
                }
                
                char cmd = line[++i]; // ++i 取得 ^ 的下一個字元
                
                if (cmd == '^'){
                    putChar('^');
                }
                else if (isdigit(cmd)){
                    int nr = cmd - '0';
                    int nc = line[++i] - '0';
                    r = nr, c = nc;
                }
                else{
                    switch (cmd){
                        case 'b': c = 0; break;
                        case 'c': clearScreen(); break;
                        case 'd': if (r < 9) ++r; break;
                        case 'e': for (int j = c; j < 10; ++j) scr[r][j] = ' '; break;
                        case 'h': r = 0, c = 0; break;
                        case 'i': insertMode = true; break;
                        case 'l': if (c > 0) --c; break;
                        case 'o': insertMode = false; break;
                        case 'r': if (c < 9) ++c; break;
                        case 'u': if (r > 0) --r; break;
                    }
                }
            }
        }
        
        cout << "Case " << test_case++ << '\n';
        cout << "+----------+\n";
        for (int i = 0; i < 10; ++i) cout << "|" << scr[i] << "|\n";
        cout << "+----------+\n";
    }
}
```