---
# System prepended metadata

title: APCS 2016 03 題解
tags: [APCS]

---

### p1. 成績指標
[題目連結](https://zerojudge.tw/ShowProblem?problemid=b964)

我們定義分數 $\ge 60$ 為 $x$ 集合，分數 $< 60$ 為 $y$ 集合
我們將其排序並從 $x$ 找出最小值 $y$ 找出最大值
如果 $x$ 沒有元素，代表沒有最小的及格成績，即為 wrost case
如果 $y$ 沒有元素，代表沒有最大的不及格成績，即為 best case

::: spoiler 程式碼
```cpp
#include <bits/stdc++.h>
using namespace std;

vector<int> m;

int main(){
    int n, minX = INT_MAX, maxY = INT_MIN;
    cin >> n;
    
    m.resize(n);
    for (auto &e: m){
        cin >> e;
        if (e >= 60)
            minX = min(minX, e);
        
        else 
            maxY = max(maxY, e);
    }

    sort(m.begin(), m.end());
    
    for (auto i: m)
        cout << i << ' ';

    cout << '\n';
    if (minX == INT_MAX)
        cout << maxY << '\n' << "worst case";

    else if (maxY == INT_MIN)
        cout << "best case" << '\n' << minX;
    
    else
        cout << maxY << '\n' << minX << '\n';
}
```
:::

### p2. 矩陣轉換

[題目連結](https://zerojudge.tw/ShowProblem?problemid=b965)

  定義 $A$ 矩陣 $r$ 為行數 $c$ 為列數
```
//值 座標(i, j)
1(0, 0), 1(0, 1) 
1(1, 0), 3(1, 1)
2(2, 0), 1(2, 1) 
```
翻轉後
```
2(0, 0), 1(0, 1)
1(1, 0), 3(1, 1)
1(2, 0), 1(2, 1)
```

旋轉後得 $B$
```
1(0, 0) 1(0, 1) 2(0, 2)
1(1, 0) 3(1, 1) 1(1, 2)
```

透過觀察位置的變化就會發現其規律。

* 翻轉及其反操作
$A$ 經過翻轉 座標變化 $(i, j)$ -> $(r-i-1, j)$

:::info
這裡的操作次數為 $r/2$ 因為每次執行會交換到兩條行數
:::

* 旋轉及其反操作
$A$ 經過旋轉 座標變化 $(i, j)$ -> $(j, r-i-1)$

:::info
這裡的 $r$ 參數已經變為控制列的 $c$ 參數
因此要將兩者的值調換，避免 $r$ 與 $c$ 變成了錯誤的行列數
:::

因題目要求因此實作時記得將操作改為反操作的形式

::: spoiler 程式碼
```cpp 
#include <bits/stdc++.h>
using namespace std;

int arr[10][10], r, c, m, ops;

void flip_back(){
    for (int i = 0; i < r/2; i++)
        for (int j = 0; j < c; j++)
            swap(arr[i][j], arr[r-i-1][j]);
}

void rotate_back(){
    int tmp[10][10];
    for (int i = 0; i < r; i++)
        for (int j = 0; j < c; j++)
            tmp[i][j] = arr[i][j];

    swap(r, c);
    for (int i = 0; i < r; i++)
        for (int j = 0; j < c; j++)
            arr[i][j] = tmp[j][r-i-1];
}

int main(){
    cin >> r >> c >> m;
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            cin >> arr[i][j];
        }
    }
    
    while (m--) {
        cin >> ops;
        if (ops)
            flip_back();

        else
            rotate_back();   
    }

    cout << r << ' ' << c << '\n';
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) 
              cout << arr[i][j] << ((j+1 == c)? "":" ");
        
        cout << '\n';
    }
}

```
:::
### p3. 線段覆蓋長度

[題目連結](https://zerojudge.tw/ShowProblem?problemid=b966)

這題我們用貪心的角度去思考，如果將線段按左界排序，我們就可以逐步判斷線段覆蓋的區間。
我們定義當前最大左右界為 $L, R$ 單一線段的左右界為 $l, r$
因為排序的關係所以我們可以保證新加入的線段 $l \ge L$。 
* 如果新加入的線段 $r \le R$，那麼覆蓋長度則不變。
* 如果新加入的線段 $l \le R$，那麼代表說 $[ l, R ]$ 這個區間已經被覆蓋了，因此我們只需要新增未被覆蓋的區間 $[ R, r ]$。
* 如果新加入的線段 $l > R$，那麼代表說 $[ l, r ]$ 這塊是完全獨立的線段，我們只需新增它即可。

實作時記得每次都要更新當前最大右界 $R$ 。
複雜度：$O(n \log n)$
::: spoiler 程式碼
```cpp 
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define l first
#define r second

priority_queue<pii, vector<pii>, greater<pii>> pq;

int main(){
    int n, a, b;
    cin >> n;
    while (n--){
        cin >> a >> b;
        pq.push({a, b});
    }

    int mr = -1, result = 0;
    while (pq.size()) {
        pii t = pq.top();
        pq.pop();
        if (t.r <= mr)
            continue;

        result += (t.l <= mr)? (t.r - mr): (t.r - t.l);
        mr = t.r;
    }

    cout << result << '\n';
}
```
:::

### p4. 血緣關係 

[題目連結](https://zerojudge.tw/ShowProblem?problemid=b967)

這題算是圖論的經典題，問你圖上距離最遠的兩個點，即為所謂的樹直徑。
由於只有詢問一次，所以只要做兩次 Depth-First-Search 就行。
至於 DFS 的實作這裡就不多作介紹。

* 第一次 DFS 的時候先搜尋離當前節點最遠的節點。
* 第二次 DFS 的時候以第一次搜尋到的點作為起點搜尋最遠的節點。

複雜度：$O(n)$

::: spoiler 程式碼
```cpp 
#include <bits/stdc++.h>
using namespace std;

vector<vector<int>> g;
int md = -1, mi = -1;

void dfs(int idx, int father, int depth){
    for (auto i: g[idx]){
        if (i != father){
           dfs(i, idx, depth+1);
        }
    }

    if (depth > md){
        md = depth;
        mi = idx;
    }
}

signed main(){
    int n, a, b;
    cin >> n;
    g.assign(n, {});
    for (int i = 1; i < n; i++){
        cin >> a >> b;
        g[a].pb(b);
        g[b].pb(a);
    }

    dfs(0, -1, 0);
    dfs(mi, -1, 0);
    cout << md << '\n';
}
```
:::
