---
# System prepended metadata

title: 2020q3 Homework10 (quiz10)
tags: [進階電腦系統理論與實作]

---

---
tags: 進階電腦系統理論與實作
---
# 2020q3 Homework10 (quiz10)
contributed by < `RinHizakura` >

> [第 10 週測驗題](https://hackmd.io/@sysprog/2020-quiz10)
> [GitHub](https://github.com/RinHizakura/NCKU-System-Software-Quiz/tree/master/quiz10)
## 測驗 `1`

## 測驗 `2`

逐行釐程式原理。

```cpp=
int *countSubgraphsForEachDiameter(int n,
                                   int **edges,
                                   int edgesSz,
                                   int *edges0sz,
                                   int *rsz)
{
    uint8_t d[n][n]; /* distance */
    memset(d, 0xFF, sizeof(d));
    uint16_t conn[n];
    ITERATE (i, 0, n)
        conn[i] = 1 << i;

   
```

* 維護二維的陣列 `d`，`d` 用來紀錄兩點間的最短距離，因此首先由 memset 初始化成 8 bits 可表示的最大距離 0xFF
    * 因為限制點最多 15 個，因此 8 bit 已經足以表示彼此的距離
* 維護陣列 `conn` bitmask，`conn[u]` 中的 bit 為 1 的位置表示點 u 可以直接到達(不需要經過其他)的點

```cpp=14
    for (int z = 0; z < edgesSz; z++) {
        int *e = edges[z];
        int u = e[0] - 1, v = e[1] - 1;
        d[u][v] = d[v][u] = 1;
        conn[u] |= 1 << v, conn[v] |= 1 << u;
    }
    
    ITERATE (k, 0, n)
        ITERATE (i, 0, n)
            ITERATE (j, 0, n)
                d[i][j] = MIN(d[i][j], d[i][k] + d[k][j]);
    int *rv = calloc(n - 1, sizeof(int));
```
* 則 for 迴圈中首先計算這些可以直接到達的點的距離和 bitmask
* 往後的 3 個 `ITERATE` 形成的迴圈中，計算每個點間接到達其他點的最短距離
    * 類似 [Floyd–Warshall algorithm](https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm) 的應用，只是在本題中 edge 的長度皆為 1，所以又更單純一些
* 接著配置回傳的空間 `rv` 

```cpp=26
     ITERATE (S, 1, (1 << n)) {
        int ctmp = 1 << __builtin_ctz(S);
        while (1) {
            int conn_nxt = ctmp;
            ITERATE (d, 0, n)
                if ((ctmp >> d) & 1)
                    conn_nxt |= conn[d];
            conn_nxt &= S;
            if (COND1)
                break;
            ctmp = conn_nxt;
        }
        if (COND2)
            continue;
        uint8_t ret = 0;
        ITERATE (i, 0, n)
            if ((S >> i) & 1)
                ITERATE (j, 0, i)
                    if (((S >> j) & 1) && (ret < d[i][j]))
                        ret = d[i][j];
        if (!ret)
            continue;
        rv[ITER]++;
    }
```
* 則可以窮舉所有 subset `S` 下最長距離，就可以有多少 subset 下的 max distance 為 1 到 (n - 1)
* 首先，要判斷此 subset 是否為 conneted，這裡使用 BFS 來判斷
    * 27 行的 `ctmp` 透過 `__builtin_ctz` 取出 S 中的第一個點為起點
    * 28 行的迴圈中，根據第 30 行的 `ITERATE` 不斷把可以連接到的點加入，判斷基於目前 `ctmp` 的點，可以再找到其他的哪些點
    * 33 行的 `conn_nxt &= S` 確保只考慮存在目標 subset `S` 中的點
    * 因此 `ctmp` 會不斷被更新，直到此次的狀態與前一次的相同，也就是 BFS 無法再往下找到新的結果，因此 `COND1 = (a) conn_nxt == ctmp`
* BFS 結束後，判斷這些點是否包含 subset 中的所有點，如果不包含，則不為滿足題意的 tree，做 continue
    * 所以對應的操作是 `COND2 = (e) ctmp ^ S`
* 如果滿足，則去判斷此 connected graph 的最大直徑，在 41 行中簡單的直接搜尋 subset 中兩點距離的最大值
* 最後則根據此最大距離去 index `rv`，注意到 rv 儲存的是 (1 到 n - 1) 長度的組合數，所以 `ITER = (d) --ret`

在 leetcode 上執行並且正確的結果

![](https://i.imgur.com/Xj8UHuu.png)
