【APCS】2024年10月實作題 C++ 解題筆記(前兩題) === 此筆記僅供個人學習用途,內容僅供參考。 1. https://zerojudge.tw/ShowProblem?problemid=o711 題目說明: 該水杯可以被視為上下兩段直立長方體所組成: 下半段:底面積 $w1 \times w1$ ,高 $h1$ 。 上半段:底面積 $w2 \times w2$ ,高 $h2$ 。 倒水時: 水先填滿下半段,接著填上半段。 每次倒入的體積 $v$ ,轉換為水位高度上升量(根據在哪一段來決定用哪段的底面積去除體積,得到水位高度)。 解題思路: 1. 先計算兩段的容量(下、上)。 2. 記錄目前水位高度。 3. 模擬每一次倒入: - 判斷水還在下半段還是已經進入上半段。 - 根據當前段的底面積換算水位上升高度。 - 水滿之後不再上升。 4. 記錄每次倒水所產生的「水位上升高度」,並取最大值輸出。 ```cpp= #include <iostream> #include <vector> using namespace std; int main() { int n; cin >> n; // 倒水次數 int w1, w2, h1, h2; cin >> w1 >> w2 >> h1 >> h2; // 下半段及上半段的寬高 vector<int> pours(n); for (int i = 0; i < n; ++i) { cin >> pours[i]; // 每次倒入的體積 } // 計算下半段與上半段的總容量 int volume1 = w1 * w1 * h1; // 下半段 int volume2 = w2 * w2 * h2; // 上半段 int totalVolume = volume1 + volume2; // 總容量 int currentVolume = 0; // 目前杯子已裝入水的總體積 int maxRise = 0; // 水位上升最高一次的高度變化量 for (int i = 0; i < n; ++i) { int v = pours[i]; // 本次所倒入的體積 int oldVolume = currentVolume; // 紀錄倒水前體積 currentVolume += v; // 更新倒水後的體積 // 判斷水是否倒滿 if (currentVolume > totalVolume) { currentVolume = totalVolume; } int rise = 0; // 該次水位上升的高度 // 判斷水在哪段(下段/上段)上升,並計算對應的高度變化 if (oldVolume < volume1 && currentVolume <= volume1) { // 落於下半段 rise = (currentVolume - oldVolume) / (w1 * w1); } else if (oldVolume < volume1 && currentVolume > volume1) { // 下半段滿了, 但有部分到達上半段 // part1 計算下半段的水位 int part1 = (volume1 - oldVolume) / (w1 * w1); // part2 計算上半段的水位 int part2 = (currentVolume - volume1) / (w2 * w2); rise = part1 + part2; } else { // 上半段全滿 rise = (currentVolume - oldVolume) / (w2 * w2); } // 更新最大水位變化 if (rise > maxRise) { maxRise = rise; } } cout << maxRise; return 0; } ``` 2. https://zerojudge.tw/ShowProblem?problemid=o712 題目說明: 你有一張 $M \times N$ 的地圖,每一格有一些寶石(或牆壁)。 有一個機器人從某個位置出發,一開始朝**右邊**,按照這些規則移動: 1. 如果格子是 $0$ 顆寶石,機器人會停下來。 2. 如果有寶石,加到分數並撿走一顆(格子上的數量 $-1$ )。 3. 如果現在的分數是 $k$ 的倍數,機器人就會 右轉 $90$ 度。 4. 如果他正前方是 牆壁或邊界外,就繼續右轉,直到找到能走的格子,然後繼續。 最後輸出總共撿了幾顆寶石。 另外: 1. 轉彎的時機是撿完寶石且加分數後。 2. 要確保走的格子 不是牆壁 (-1) 且 不出界。 解題思路: 1. 設二維 vector 變數 grid 存地圖。 2. 用一個向量陣列來記錄方向 dx, dy,代表四個方向(右、下、左、上)。 3. 機器人邏輯設計: - 如果當前位置寶石數是 0,停止。 - 否則將寶石數加到 score,並將該格寶石數量減 1。 - 如果 score % k == 0,右轉(方向 +1)。 - 往該方向走,如果遇到牆壁或出界,就持續右轉直到可以走為止。 ```cpp= #include <iostream> #include <vector> using namespace std; int main() { int M, N, k, r, c; cin >> M >> N >> k >> r >> c; vector<vector<int>> grid(M, vector<int>(N)); for (int i = 0; i < M; ++i) for (int j = 0; j < N; ++j) cin >> grid[i][j]; // 右 下 左 上 int dx[4] = {0, 1, 0, -1}; // 0 : 不動, 1: 動一格, -1: 退一格 int dy[4] = {1, 0, -1, 0}; // dx 為行與行(row) 之間的移動, 會跳到下一個 vector // dy 為列與列(column) 之間的移動, 會在同一個 vector 中不同元素跳動 int dir = 0; // 紀錄目前方向, 預設朝右 int score = 0, gems = 0; while (true) { // 如果當前格子寶石為 0, 跳出迴圈 if (grid[r][c] == 0) break; // 加分與撿寶石 score += grid[r][c]; grid[r][c]--; gems++; // 是否右轉 if (score % k == 0) dir = (dir + 1) % 4; // 找下一個可以走的方向 for (int i = 0; i < 4; ++i) { // 嘗試從目前方向 dir 移動,計算新位置 (nr, nc) int nr = r + dx[dir]; int nc = c + dy[dir]; // 邊界檢查 + 檢查是否遇到牆 (grid[nr][nc] != -1) if (nr >= 0 && nr < M && nc >= 0 && nc < N && grid[nr][nc] != -1) { r = nr; c = nc; break; } dir = (dir + 1) % 4; // 右轉 } } cout << gems; return 0; } ```