---
# System prepended metadata

title: 2024/10 APCS 實作題

---

# 2024/10 APCS 實作題


# 第一題 - 裝飲料
:::info
https://zerojudge.tw/ShowProblem?problemid=o711
:::


## 題目
有一個杯子，可將其體積視為由兩個長方 體組成（如下圖），下方的長方體底面積 為 $w_1 \times w_1$ cm$^2$，高為 $h_1$ cm，上方的長方體底面積為 $w_2 \times w_2$ cm$^2$，高為 $h_2$ cm。       
一開始杯子為空。要裝 $n$ 次飲料，每一次裝 $v$ cm$^3$ 容量的飲料，當水杯滿 時水位不再上升。問這 $n$ 次倒飲料中水位上升變化量最高是幾 cm。

## 輸入說明:
第一行有一個正整數 $n (1 \le n \le 10)$ 代表要裝 $n$ 次飲料。接下來一行有 4 個正整數 $w_1, w_2, h_1, h_2 (1 \le w_1, w_2, h_1, h_2 \le 50)$ 代表杯子的寬度與高度。最後一行有 $n$ 個正整數代表每次裝飲料的容量。保證每次水位上 升都是整數。
(60%) n = 1，即答案為倒水後的高度    
(40%) 無限制

## 輸出說明:
輸出這 $n$ 次倒水中，上升變化量最大的高度為何。
## 解題絲路
說實話其實我一開始根本看不懂題目...講來講去到底要幹嘛還是不清楚，光是讀懂題目就花了一段時間...

白話文:
杯子的體積是一大一小兩個長方形體積相加，連續倒n次水，哪次會使水位升高最多(溢出的就不算)。
> 
> 以測資2舉例:
> 第一次倒 400：水位從 0 → 13（上升 13）
> 第二次倒 600：水位從 13 → 19（上升 6）
> 輸出13。

所以想法其實很簡單，就只有三種情況:
1. **V<=G1**: 如果此時倒水體積(V)小於小長方體(G1)，那增加的高度就只有 $(V/G1)\times h1$
2. **G1<=V<=G2+G1**: 如果此時的體積(V)大於G1卻小於G2，則增加高度為$h1 + ((V-G1)/G2)\times h2$
3. **G1+G2<V**: 如果此時的體積(V)大於G1+G2也就是大於整個杯子，則水會溢出來，$h1 +  h2$
```cpp=
#include <bits/stdc++.h>
#define io ios::sync_with_stdio(0);cin.tie(0);
using namespace std;

int main(){

    int n;
    cin >> n;

    double w1,w2,h1,h2;
    cin >> w1>> w2>>h1>>h2;

    double G1 = w1*w1*h1;
    double G2 = w2*w2*h2;

    double mx=0,V=0,H=0;
    for (int i=0;i<n;++i){
        double a;
        cin >> a;

        double oldH=H;
        V+=a;
        
        if (V <= G1)H=V/G1*h1;
        else if (V <= G1 + G2) H = h1 + (V - G1) / G2*h2;
        else H=h1+h2;

        mx = max(mx, H-oldH);
    }
    cout << mx;
}
```
:::warning
因為C++除法的特性，所以建議都用double。
:::

# 第二題 - 蒐集寶石
2024/10 實作第二題
:::info
題目:
{%preview https://zerojudge.tw/ShowProblem?problemid=o712 %}
:::
# 題目
有一個 $M \times N$ 的地圖，每一格的數字紀錄著寶石的數量，如果數字是 -1 代表牆壁。 
有一位機器人一開始位於 $(r, c)$ 的位置上且方向朝右邊，他遵循著以下規則行走。       
1. 若機器人位於的格字內寶石數量為 $0$，則機器人程式終止。
2. 機器人維護著一個分數 score，將 score 加上當前格的寶石數量，並且撿起一顆寶石。   
3. 若 score 是 $k$ 的倍數，則向右轉 90 度。
4. 若機器人面向的格子是牆壁或是超出邊界，則繼續向右轉 90 度直到面向的格子非牆壁或非超出邊界，並回到第 1 步。


例如機器人一開始在座標 $(2, 1)$ 且 $k = 2$，向右走兩步之後分數為 $3 + 2 + 3 = 8$， 由於 $8$ 是 $2 (k = 2)$ 的倍數所以向右轉 $90$ 度。接下來往下走一步分數變為 $11$，需要向右轉 $2$ 次 $90$ 度才不會面向牆壁或是邊界外的格子。
接下來向前走一步走到座標 $(2, 3)$，由於先前已經拿走一顆寶石，該位置的寶石數量變為 $2$，因此分數變為 $13$，再繼續往上走兩步到 $(0, 3)$ 處分數為 $16$，由於 $16$ 為 $2 (k = 2)$ 的倍數所以向右轉 $90$ 度。
向前走一格到 $(0, 4)$ 後需要向右轉兩次 $90$ 度，回到 $(0, 3)$ 後由於寶石數量為 $0$ ，機器人停止。過程中機器人總共撿了 $8$ 顆寶石。

## 輸入說明:
第一行有 $5$ 個正整數 $M, N, k, r, c$
$1 \le M \le 100$
$2 \le N \le 100$
$1 \le k \le 20$
$0 \le r < m$
$0 \le c < n$
保證機器人初始位置不是牆壁。接下來有 $M$ 行，每一行有 $N$ 的數字，代表地圖的資訊。 
(60%) M = 1
(40%) 無限制

## 輸出說明:
輸出機器人會蒐集幾個寶石。

---
格狀圖的應用，只要記得下面這幾件事:
1. 用moves代表移動，dir來實現移動。
2. 結束條件多-> 用另一個函式來判斷是否符合結束條件。
3. 因為要一直執行，用無限迴圈。
4. 用原生array建立格狀圖

## 初步格式
```cpp=
# include <bits/stdc++.h>
# define io ios::sync_with_stdio(0),cin.tie(0);
using  namespace std;

int graph[101][101] ={};
int moves[4][2]={{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

int end(){} // 如果要結束就回傳 1

int main(){io;
    memset(graph,-1,sizeof(graph));
    // 因為有預留牆壁的位置所以迴圈從1開始跑
    for (int i=1;i<=M;++i){
	for (int j=1;j<=N;++j){
		cin >> graph[i][j];
        }
    }
    int score=0,gem=0;
    
    while(true){
        if (end())break;
        if (!(score%k))dir=(dir+1)%4;
    }
    
    
    return 0;
}
```
:::warning
要注意因為有給牆壁留空間，所以起始位置各要++
:::
**轉彎規則如下:**
1.score是k倍數。
2.面向牆壁要無條件一直轉，直到不是面向牆壁。

所以除了上面程式寫到的`if (!(score%k))dir=(dir+1)%4;`，我們還ˊ需要另一個while來一直判斷此時是否面向牆壁。

## 解
```cpp=
# include <bits/stdc++.h>
# define io ios::sync_with_stdio(0),cin.tie(0);
using  namespace std;

int graph[102][102] ={};
int moves[4][2]={{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

int end(int r,int c){
    if (graph[r][c]==0)return 1;

    int ok=4;
	for (int i=0;i<4;i++)  //判斷四面是否都是牆壁
		if (graph[r+moves[i][0]][c+moves[i][1]] == -1)
			ok--;

	if (ok == 0)  //四面都是牆壁
		return 1;

	return 0;
}
int main(){io;
    int M,N,k,r,c;
    cin >> M >> N >> k>>r>>c;

    memset(graph,-1,sizeof(graph));

    // 因為有預留牆壁的位置，所以起始位置要++
    r++;
    c++;


	for (int i=1;i<=M;++i){
		for (int j=1;j<=N;++j){
			cin >> graph[i][j];
        }
	}

    int score=0,gem=0,dir=0;

    while (true){
        if (end(r,c))break;

        score+=graph[r][c];
        graph[r][c]--;
        gem+=1;

        if (!(score%k))dir=(dir+1)%4;

        int temp_r,temp_c;
        while (true){
            // 如果現在的方向是面對牆壁，要轉到正確的方向
            temp_r = r+moves[dir][0];
            temp_c = c+moves[dir][1];
            if (graph[temp_r][temp_c]==-1)dir=(dir+1)%4;
            else break;
        }
        r = temp_r;
        c = temp_c;
    }
    cout << gem;
    return 0;
}
```

---

:::info
趁機宣傳一下我自己的個人網站跟Youtube頻道 !!

**[個人網站](https://hyc.eshachem.com/) | [Youtube頻道](https://www.youtube.com/@Hy.C)**
:::
@2025 Hy.C 陳毓
> Copyright ©Hy.C 陳毓 CC BY-NC-SA 4.0 | 禁止商業用途 | 轉載標記出處 | 改編作品必須在相同條款下分享。