# APCS實作題2024年1月第2題:蜜蜂觀察
> 日期:2024年1月29日
> 作者:王一哲
> 題目來源:2024年1月實作題第2題
> [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=m932)
## 題目
### 問題描述
蜜蜂 Bob 在一個大小是 $m \times n$ 的蜂巢 (見範例說明圖示),並且每個蜂巢格上會有一個代表字母(大小或小寫英文字母)。
Bob 一開始在蜂巢左下角,行走方向定義如圖:0 是往右上; 1 是往右邊; 2 是往右下; 3 是往左下; 4 是往左邊; 5 是往左上。
輸入每步移動的方向,請輸出經過的路徑每格的代表字母,以及經過字元的種類數(大小寫相異),若經過時碰到牆壁該行動會停在原地。
<br />
<img height="30%" width="30%" src="https://imgur.com/F3Wvoxd.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<br /><br />
### 輸入說明
第一行包含三個整數 $m$、$n$、$k$,$(1 \leq m, n \leq 20)$,$(1 \leq k \leq 100)$,表示蜂巢的大小是 $m \times n$,Bob 的行走路徑有 $k$ 步。
接下來的 $m$ 行,每行包含 $n$ 個字母(大小寫英文字母),代表蜂巢的狀態。
最後一行包含 $k$ 個整數,表示 Bob 的移動路徑方向。
子題分數:
- 60%:滿足 $m=2$。
- 40%:無額外限制。
<br />
### 輸出格式
各輸出一行,包含兩個部分:
- 由 Bob 每步經過的每格代表字母所組成的字串。
- 經過字元的種類數(大小寫視為相異),用一個整數表示。
<br />
### 範例輸入1
```
2 4 5
TyuI
ABaB
0 1 2 3 0
```
<img height="35%" width="35%" src="https://imgur.com/rOL6X1h.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<br /><br />
### 範例輸出1
```
Tyaau
4
```
<br />
### 範例輸入2
```
4 6 11
rMmnis
LRveEX
ZexDoc
HAdbHA
0 1 5 1 1 0 3 0 0 1 0
```
<img height="60%" width="60%" src="https://imgur.com/mA5GKy2.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<br /><br />
### 範例輸出2
```
ZeLRvmvmmnn
7
```
<br />
## Python 程式碼
在題目給的蜂巢資料最右側、最下方加上不是英文字母的字元作為哨兵,以下的程式中使用的是 "@",如果讀到這個字元代表已出界,這樣的作法會比檢查索引值本身還簡單。由於 Python 的串列索引值 -1 會對應到最後一個元素,所以只要圍右側、下方即可。費時最久約為 19 ms,使用記憶體最多約為 3.4 MB,通過測試。
```python=
m, n, k = map(int, input().split()) # 讀取 m, n, k
hive = [list(input()) for _ in range(m)] # 讀取蜂巢資料
steps = list(map(int, input().split())) # 讀取行走步驟資料
for i in range(m): hive[i].append("@") # 於每一列最後面加上 @ 作為哨兵
hive.append(["@"]*(n+1)) # 於最下方加上 n+1 個 @ 作為哨兵,可能往右下方移動,如果只加 n 個 @ 可能會出界
ans = "" # 路徑上的字母,預設為空字串
chars = set() # 集合,儲存出現過的字母
r, c = m-1, 0 # 起始位置
dirs = ((-1, 0), (0, 1), (1, 1), (1, 0), (0, -1), (-1, -1)) # 移動方向,分別代表右上、右、右下、左下、左、左上
for step in steps: # 由 steps 依序讀取移動方向
if hive[r + dirs[step][0]][c + dirs[step][1]] != "@": # 先檢測下一格是否出界,如果沒出界就更新位置
r += dirs[step][0]
c += dirs[step][1]
ch = hive[r][c] # 讀取這格的字母
ans += ch # 將 ch 加到 ans 最後面
chars.add(ch) # 將 ch 加入集合 chars
print(ans) # 印出路徑上的字母
print(len(chars)) # 印出不同的字母數量
```
<br /><br />
## C++ 程式碼
在題目給的蜂巢資料四周加上不是英文字母的字元作為哨兵,以下的程式中使用的是 "@",如果讀到這個字元代表已出界,這樣的作法會比檢查索引值本身還簡單。由於 C++ 的 vector 索引值不能是 -1,這樣會遇到 IndexError。費時最久約為 2 ms,使用記憶體最多約為 336 kB,通過測試。
```cpp=
#include <iostream>
#include <vector>
#include <string>
#include <set>
using namespace std;
int main() {
int m, n, k; cin >> m >> n >> k; // 讀取 m, n, k
/* 蜂巢資料,産生 (m*2) * (n+2) 的二維 vector, *
* 預設值皆為 @,由測資讀取資料時再取代對應的位置。 */
vector<vector<char>> hive (m+2, vector<char> (n+2, '@'));
for(int i=1; i<=m; i++) {
for(int j=1; j<=n; j++) {
char ch; cin >> ch;
hive[i][j] = ch;
}
}
vector<int> steps (k, -1); // 行走步驟資料
for(int i=0; i<k; i++) {
int step; cin >> step;
steps[i] = step;
}
string ans = ""; // 路徑上的字母,預設為空字串
set<char> chars; // 集合,儲存出現過的字母
int r = m, c = 1; // 起始位置為左下角,因為四周有加上哨兵,列、欄索引值都要加1
int dirs[6][2] = {{-1, 0}, {0, 1}, {1, 1}, {1, 0}, {0, -1}, {-1, -1}}; // 移動方向,分別代表右上、右、右下、左下、左、左上
for(int i=0; i<k; i++) { // 由 steps 依序讀取移動方向
int step = steps[i];
if (hive[r + dirs[step][0]][c + dirs[step][1]] != '@') { // 先檢測下一格是否出界,如果沒出界就更新位置
r += dirs[step][0];
c += dirs[step][1];
}
char ch = hive[r][c]; // 讀取這格的字母
ans += ch; // 將 ch 加到 ans 最後面
chars.insert(ch); // 將 ch 加入集合 chars
}
cout << ans << "\n"; // 印出路徑上的字母
cout << chars.size() << "\n"; // 印出不同的字母數量
return 0;
}
```
<br /><br />
---
###### tags:`APCS`、`Python`、`C++`