這題「連鎖反應(APCS 2024-10)」是 APCS 近年中高難度模擬題之一。
下面我幫你整理出一份 完整 C++17 解答 + 超詳細中文註解版本,
適合用於 教學講義 或 班級講解。
#include <bits/stdc++.h>
using namespace std;
// 🧭 方向位移:下、上、右、左
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
int M, N, Q; // M:列數, N:行數, Q:希望被觸發的陷阱數量
vector<vector<int>> grid; // 地圖資訊
int sx, sy; // 寶藏的位置座標 (start x, start y)
// 🧩 simulate(): 模擬當初始半徑為 init 時,能觸發多少陷阱
int simulate(int init) {
// vis[i][j]:該格是否已被觸發過 (避免重複計算)
vector<vector<int>> vis(M, vector<int>(N, 0));
queue<pair<int,int>> q; // 外層 BFS:記錄會再引發「次級爆炸」的陷阱
vis[sx][sy] = 1;
q.push({sx, sy}); // 寶藏起始陷阱
int cnt = 1; // 計算觸發的陷阱總數
// 🔹 內部 BFS 模擬「爆炸波」擴散
auto bfsTrigger = [&](int r, int c, int rad) {
queue<pair<int,int>> q2; // q2:目前這顆陷阱的波傳佇列
vector<vector<int>> dist(M, vector<int>(N, -1)); // 記錄距離(步數)
q2.push({r, c});
dist[r][c] = 0;
while (!q2.empty()) {
auto [x, y] = q2.front();
q2.pop();
// ⚡ 若這格還沒被觸發 → 標記為已觸發
if (!vis[x][y]) {
vis[x][y] = 1;
cnt++; // 總觸發陷阱數 +1
// 若該陷阱本身半徑 > 0,表示它也能引發其他陷阱爆炸
if (grid[x][y] > 0)
q.push({x, y}); // 加入外層 BFS,稍後再觸發
}
// 🔄 從這格向四周擴散
for (int d = 0; d < 4; d++) {
int nx = x + dx[d];
int ny = y + dy[d];
// 🧱 邊界與阻擋條件
if (nx < 0 || ny < 0 || nx >= M || ny >= N) continue; // 超出地圖
if (grid[nx][ny] == -1) continue; // 石頭,訊號不能穿過
if (dist[nx][ny] != -1) continue; // 已走過
if (dist[x][y] + 1 > rad) continue; // 超過半徑範圍
// ✅ 可以走的格子:記錄距離並加入佇列
dist[nx][ny] = dist[x][y] + 1;
q2.push({nx, ny});
}
}
};
// 🌀 第一次觸發寶藏陷阱
bfsTrigger(sx, sy, init);
// 🔁 外層 BFS:處理「連鎖反應」
while (!q.empty()) {
auto [x, y] = q.front();
q.pop();
bfsTrigger(x, y, grid[x][y]); // 觸發每一顆被引爆的陷阱
}
return cnt; // 回傳觸發陷阱總數
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> M >> N >> Q;
grid.assign(M, vector<int>(N, 0));
// 🧭 讀取地圖
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
cin >> grid[i][j];
if (grid[i][j] == -2) { // -2 = 寶藏位置
sx = i;
sy = j;
}
}
}
// 🧮 用二分搜尋找出最小半徑
int L = 0, R = 30, ans = -1;
while (L <= R) {
int mid = (L + R) / 2; // 試探半徑
if (simulate(mid) >= Q) { // 若已達到 Q 個陷阱
ans = mid; // 記錄這個半徑
R = mid - 1; // 嘗試更小的半徑
} else {
L = mid + 1; // 半徑太小,擴大範圍
}
}
cout << ans << "\\n"; // 輸出最小可行半徑
return 0;
}
2 → 寶藏(起點陷阱)1 → 石頭(訊號不能穿過)0 → 陷阱但沒有連鎖效果>0 → 可觸發其他陷阱的陷阱(半徑 = 數字)simulate(init) 的功能這個函式做的事是:
給定一個初始半徑 init,模擬整個爆炸連鎖過程,回傳最後被觸發的陷阱總數。
流程:
(sx, sy)。