---
# System prepended metadata

title: Weekly Contest 391

---

# Weekly Contest 391

日期： 2024/03/31


這是我第一次去做 LeetCode Weekly Contest，一題 Easy、兩題 Medimum、一題 Hard，除了 Hard 沒想出來之外，其它題目約在 5 分鐘內解出來。

此次題目（測驗當下題目不會顯示標題）：
- [3099. Harshad Number](https://leetcode.com/problems/harshad-number/description/)
- [3100. Water Bottles II](https://leetcode.com/problems/water-bottles-ii/description/)
- [3101. Count Alternating Subarrays](https://leetcode.com/problems/count-alternating-subarrays/description/)
- [3102. Minimize Manhattan Distances](https://leetcode.com/problems/minimize-manhattan-distances/description/)

第一題跟第二題只要按照題目敘述去做即可解決
#### 第一題
```clike
class Solution {
public:
    int sumOfTheDigitsOfHarshadNumber(int x) {
        int y = x;
        int s = 0;
        while(y) {
            s += y % 10;
            y /= 10;
        }
        if(x % s == 0)
            return s
        return -1;
    }
};
```

#### 第二題
```clike
class Solution {
public:
    int maxBottlesDrunk(int numBottles, int numExchange) {
        int ret = 0;
        int empty_bottles = 0;
        while(true) {
//             drink
            ret += numBottles;
            empty_bottles += numBottles;
            numBottles = 0;
            if(empty_bottles >= numExchange) {
//                 Exchange
                numBottles++;
                empty_bottles -= numExchange;
                numExchange++;
            }else{
                break;
            }
        }
        return ret;
    }
};
```

#### 第三題
```clike
class Solution {
public:
    long long countAlternatingSubarrays(vector<int>& nums) {
        using ll = long long;
        const int n = nums.size();
        if(n <= 1)
            return n == 1;
        
        ll ret = 0;
        
        int i = 0;
        int j = 1;
        
        while(i < n) {
            while(j < n && nums[j] != nums[j - 1]) {
                j++;
            }
            
            ll length = j - i;
            ret += ((length + 1) * length) / 2;
            
            i = j;
            j++;
        }
        
        
        return ret;
    }
};
```

從題目給予的範例來看：
- `[0, 1, 1, 1]` 符合的有 `[0], [1], [1], [1], [1]` 總共 5 個
- `[1, 0, 1, 0]` 有 10 組，任意子陣列都符合
    - 取 1 個：`[1], [0], [1], [0]`
    - 取 2 個：`[1, 0], [0, 1], [1, 0]`
    - 取 3 個：`[1, 0, 1], [0, 1, 0]`
    - 取 4 個：`[1, 0, 1, 0]`
    - 4 + 3 + 2 + 1 得出計算總和公式：`(length + 1) * length / 2`

解題當下我多想了一組測試資料：`[1, 0, 1, 0, 0]`，總共 11 個。
比起上面的在最後多了個 `0`，答案只多了個 `[0]`，與 `[1, 0, 1, 0]` 是分開計算的子陣列。
因此陣列可以切割成數個子陣列，再利用子陣列長度計算答案的數量。


#### 第四題
這裡的程式碼是當下想到的，我自己從題目限制得到一些線索，演算法 $O(N^2)$ 可能可以接受。只是目前只能做出 TLE 版本的。
```clike
class Solution {
public:
    int minimumDistance(vector<vector<int>>& points) {
        const int n = points.size();
        vector<vector<int>> dd(n, vector<int>(n, 0));
        
        for(int i = 0 ; i < n; i++) {
            for(int j = 0; j < n ; j++) {
                int x1 = points[i][0];
                int y1 = points[i][1];
                
                int x2 = points[j][0];
                int y2 = points[j][1];
                int diff = abs(x1 - x2) + abs(y1 - y2);
                dd[i][j] = diff;
                dd[j][i] = diff;
            }
        }
        
        int ret = INT_MAX;
        
        for(int rp = 0; rp < n; rp++) {
//             rp --> removed points
            
            int local_max = 0;
            for(int i = 0; i < n; i++) {
                if(i == rp)
                    continue;
                for(int j = 0; j < n; j++) {
                    if(j == rp)
                        continue;
                    local_max = max(local_max, dd[i][j]);
                }
            }
            
            ret = min(ret, local_max);
        }
        
        return ret;
    }
};
```

這題考的是知識面，知道的就能做出來，參考 [Youtube 官神影片](https://www.youtube.com/live/cumosO16uB4)，影片看完就發現概念很簡單。

根據題目敘述，The Manhattan Distance between two cells $(x_i, y_i)$ and $(x_j, y_j)$ is $|x_i - x_j| + |y_i - y_j|$
可以將絕對值拆解成四個數字：
- $(x_i - x_j) + (y_i - y_j)$
- $(x_i - x_j) + (y_j - y_i)$
- $(x_j - x_i) + (y_i - y_j)$
- $(x_j - x_i) + (y_j - y_i)$

使用 C++ 的四個 `multiset` 去儲存這些數值，計算後即為 Manhattan Distance。另，題目要求移除一顆的答案，只要遍歷每個點，移除該數值後再取 Manhattan Distance 即題目要求，之後把將該點加回去，繼續下次計算。

```clike
class Solution
{
public:
    int minimumDistance(vector<vector<int>> &points)
    {
        if (points.size() <= 1)
            return 0;

        vector<multiset<int>> arr(4);
        for (vector<int> &p : points)
        {
            int t[4] = {p[0] + p[1], p[0] - p[1],
                        -p[0] + p[1], -p[0] - p[1]};
            for (int i = 0; i < 4; i++)
                arr[i].insert(t[i]);
        }

        int ret = INT_MAX >> 1;
        for (vector<int> &p : points)
        {
            int t[4] = {p[0] + p[1], p[0] - p[1],
                        -p[0] + p[1], -p[0] - p[1]};
//             移除該點數值
            for (int i = 0; i < 4; i++)
                arr[i].erase(arr[i].find(t[i]));
//             計算答案
            int local_max = 0;
            for (int i = 0; i < 4; i++) {
                int diff = *prev(arr[i].end()) - *arr[i].begin();
                local_max = max(local_max, diff);
            }

            ret = min(ret, local_max);
//             加回去
            for (int i = 0; i < 4; i++)
                arr[i].insert(t[i]);
        }
        return ret;
    }
};
```

看解答區還有更好的演算法，我自己是覺得這類型的題目這樣就好。