這題「連鎖反應(APCS 2024-10)」是 APCS 近年中高難度模擬題之一。

下面我幫你整理出一份 完整 C++17 解答 + 超詳細中文註解版本

適合用於 教學講義班級講解


💥 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;
}


🧠 詳細說明與程式邏輯圖


(1) 題目核心概念


(2) simulate(init) 的功能

這個函式做的事是:

給定一個初始半徑 init,模擬整個爆炸連鎖過程,回傳最後被觸發的陷阱總數。

流程:

  1. 先觸發寶藏陷阱 (sx, sy)
  2. 用 BFS 模擬爆炸波在半徑範圍內擴散。