13.4 一步一步走:模擬移動與轉向
有了 inside 和方向表,「一個角色在地圖上聽規則行動」就只是把規則排好、一條一條翻譯。角色的狀態是三個變數:位置 r、c 與面向 dir;每一步固定的節奏是先算下一格、檢查、再移動——不要先 r += dr[dir] 再檢查,撞牆時人已經站在牆裡了。
int nr = r + dr[dir], nc = c + dc[dir]; // 先算「下一格」,不動 r、c
if (inside(nr, nc) && grid[nr][nc] != '#') { // 檢查
r = nr; c = nc; // 通過才踏上去
}
¶例題:蒐集寶石(APCS 2024 年 10 月中級)
題目就是「想像一下」那五條規則。1 \le M \le 100、2 \le N \le 100、2 \le K \le 20,寶石數介於 0 \sim K-1,起點不是牆且四周不會全無路可走,保證機器人一定會停。輸出的是拿到的寶石數,不是得分。
規則已經幫你排好順序了,程式的主迴圈就照 1~5 寫,一條規則一段註解:
#include <bits/stdc++.h>
using namespace std;
const int MAX_SIZE = 100;
// 方向照「右轉一次」的順序排:東 → 南 → 西 → 北
// 右轉就是 dir 加 1(繞一圈回到東)
const int dr[4] = {0, 1, 0, -1};
const int dc[4] = {1, 0, -1, 0};
int main() {
int M, N, K, r, c;
cin >> M >> N >> K >> r >> c;
int grid[MAX_SIZE][MAX_SIZE];
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
cin >> grid[i][j]; // -1 是牆,其餘是寶石數
}
}
int dir = 0; // 0 = 東、1 = 南、2 = 西、3 = 北
int score = 0; // 總得分
int gems = 0; // 拿到的寶石數(答案)
while (grid[r][c] != 0) { // 規則 1:這一格沒寶石就停止
score += grid[r][c]; // 規則 2:得分加上這一格目前的寶石數,
grid[r][c] -= 1; // 並拿走一顆
gems += 1;
if (score % K == 0) { // 規則 3:得分是 K 的倍數就右轉
dir = (dir + 1) % 4;
}
while (true) { // 規則 4:前方出界或是牆,就一直右轉
int nr = r + dr[dir];
int nc = c + dc[dir];
if (nr >= 0 && nr < M && nc >= 0 && nc < N && grid[nr][nc] != -1) {
break; // 前方走得進去,右轉到此為止
}
dir = (dir + 1) % 4;
}
r += dr[dir]; // 規則 5:走進前方的格子
c += dc[dir];
}
cout << gems << endl;
return 0;
}
幾個值得停下來看的地方:
- 方向表從「東」開始排(題目說初始面向東),但一樣順時針,右轉一樣
+ 1。 - 規則 4 的
while (true)一定會停:機器人是從某個方向走進來的,往回走的那個方向一定通。 - 會不會跑不完?每一輪都拿走一顆寶石,寶石總數有限(不超過 100 \times 100 \times 19),所以迴圈次數有上界。