置頂

我的 VPython 教學文件 (HackMD 版本)

VPython 教學文件目錄 安裝及測試 基本語法 等速度直線運動 自由落下 終端速度 水平抛射 使用For迴圈計算水平抛射資料 斜向抛射 圓周運動 簡諧運動 單擺 木塊彈簧系統分離 重力及簡諧 行星運動 相疊木塊 雙重簡諧運動 一維彈性碰撞 ...

熱門文章

2026年11月3日 星期二

ZeroJudge 解題筆記:t166.青蛙跳石

作者:王一哲
日期:2026年11月3日


ZeroJudge 題目連結:t166.青蛙跳石

解題想法


每組測資第一列為兩個整數 $n ~(2 \leq n \leq 2 \times 10^5), k ~(0 \leq t \leq 1000)$,分別代表有 $n$ 個石頭,一次最多可以往前跳 $k$ 個石頭;第二列有 $n$ 個整數 $h_i ~(0 \leq h_i \leq 10^9)$,分別代表第 $i$ 個石頭的高度。從第 $i$ 個石頭跳到第 $j$ 個石頭需要的能量為 $| h_i - h_j|$。求從第 $0$ 個石頭跳到第 $n-1$ 個石頭需要的最低能量。

這題考動態規畫。開一個長度為 $n$ 的一維陣列 $dp$,$dp[i]$ 代表跳到第 $i$ 個石頭需要的最低能量,預設為無窮大,另外 $dp[0] = 0$。用兩層 for 迴圈更新 $dp$,外層 for 迴圈跑起點 $i = 0$ 到 $i = n-2$;內層 for 迴圈跑終點 $j = i+1$ 到 $j = min(i+k+1, n) - 1$;如果 $dp[i] + abs(hs[i] - hs[j]) < dp[j]$,找到新的最低成本,更新 $dp[j]$。最後答案在 $dp[n-1]$。

Python 程式碼


使用時間約為 12 ms,記憶體約為 9.6 MB,通過測試。
def solve():
    import sys
    from bisect import bisect_left

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        k = int(data[ptr + 1])
        ptr += 2
        hs = tuple(map(int, data[ptr : ptr + n]))
        ptr += n

        # dp[i] 代表跳到第 i 個石頭的最低總成本,預設為無窮大
        dp = [float('inf')] * n
        dp[0] = 0  # 起點,成本 0
        # 測試從第 i 個石頭往後跳到第 j 個石頭是否為新的最低總成本
        for i in range(n-1):
            for j in range(i+1, min(i+k+1, n)):
                dp[j] = min(dp[j], dp[i] + abs(hs[i] - hs[j]))
        # 答案在 dp[n-1]
        result.append(f"{dp[n-1]:d}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


LeetCode 解題筆記:736. Minimum Moves to Equal Array Elements III

作者:王一哲
日期:2026年11月3日


LeetCode 題目連結:3736. Minimum Moves to Equal Array Elements III

解題想法


簡單題。題目給一個長度為 $n$ 的陣列 $nums$,每次操作可以將陣列中一個數字加 1,至少要操作幾次才能夠讓陣列中每一項都一樣大?

如果原來的陣列最大值為 $imax$,目標就是把所有的數字都補到 $imax$,因此答案 $$ ans = \sum_{i = 0}^{n-1} (imax - nums[i]) = n \times imax - \sum_{i = 0}^{n-1} nums[i] $$

Python 程式碼


Runtime: 2 ms, beats 43.08%. Memory: 19.21 MB, beats 39.49%.
class Solution:
    def minMoves(self, nums: List[int]) -> int:
        return len(nums) * max(nums) - sum(nums)


Runtime: 1 ms, beats 57.44%. Memory: 19.08 MB, beats 92.56%.
class Solution:
    def minMoves(self, nums: List[int]) -> int:
        imax = max(nums)
        return sum(imax - num for num in nums)


C++ 程式碼


Runtime: 1 ms, beats 27.20%. Memory: 31.46 MB, beats 79.45%.
class Solution {
public:
    int minMoves(vector<int>& nums) {
        return nums.size() * (*max_element(nums.begin(), nums.end())) - accumulate(nums.begin(), nums.end(), 0);
    }
};


Runtime: 0 ms, beats 100.00%. Memory: 31.60 MB, beats 45.60%.
class Solution {
public:
    int minMoves(vector<int>& nums) {
        int imax = *max_element(nums.begin(), nums.end());
        int ans = 0;
        for(int num : nums) {
            ans += imax - num;
        }
        return ans;
    }
};


Runtime: 0 ms, beats 100.00%. Memory: 31.22 MB, beats 98.43%.
class Solution {
public:
    int minMoves(vector<int>& nums) {
        int imax = 0;
        for(int num : nums) {
            imax = max(imax, num);
        }

        int ans = 0;
        for(int num : nums) {
            ans += imax - num;
        }

        return ans;
    }
};


2026年11月2日 星期一

ZeroJudge 解題筆記:t165.末班公車

作者:王一哲
日期:2026年11月2日


ZeroJudge 題目連結:t165.末班公車

解題想法


每組測資第一列為兩個整數 $n ~(1 \leq n \leq 2 \times 10^5), t ~(0 \leq t \leq 10^9)$,代表有 $n$ 班公車,人抵達公車站的時刻為 $t$;第二列有 $n$ 個整數 $d_i ~(0 \leq d_i \leq 10^9)$,分別代表第 $i$ 班公車發車時刻。印出最短的等車時間,如果沒有車能搭則印出 $-1$。

這題如果知道內建的排序及二分搜工具寫起來很快。先將發車時刻存入陣列 $ds$,將 $ds$ 由小到大排序。再用 Python 的 bisect_left 或是 C++ 的 lower_bound 從 $ds$ 找第一個大於、等於 $t$ 的索引值 $idx$,如果 $idx == n$ 代表找不到,印出 $-1$;反之,印出 $t - ds[idx]$。

Python 程式碼


使用時間約為 11 ms,記憶體約為 9.4 MB,通過測試。
def solve():
    import sys
    from bisect import bisect_left

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        t = int(data[ptr + 1])
        ptr += 2
        ds = sorted(map(int, data[ptr : ptr + n]))
        ptr += n
        idx = bisect_left(ds, t)
        if idx == n:
            result.append("-1\n")
        else:
            result.append(f"{ds[idx] - t:d}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


LeetCode 解題筆記:462. Minimum Moves to Equal Array Elements II

作者:王一哲
日期:2026年11月2日


LeetCode 題目連結:462. Minimum Moves to Equal Array Elements II

解題想法


中等難度題。題目給一個長度為 $n$ 的陣列 $nums$,每次操作可以將陣列中一個數字加 1 或減 1,至少要操作幾次才能夠讓陣列中每一項都一樣大?

這題考數學。如果原來的陣列中位數為 $mid$,操作次數等於每個數字與 $mid$ 差值的絕對值加總。先將 $nums$ 排序,中位數 $mid = nums[n/2]$,再用迴圈計算 $abs(nums[i] - mid)$ 的加總。

Python 程式碼


Runtime: 2 ms, beats 89.42%. Memory: 20.24 MB, beats 93.87%.
class Solution:
    def minMoves2(self, nums: List[int]) -> int:
        n = len(nums)
        nums.sort()
        mid = nums[n//2]
        return sum(abs(num - mid) for num in nums)


C++ 程式碼


Runtime: 0 ms, beats 100.00%. Memory: 15.58 MB, beats 96.89%.
class Solution {
public:
    int minMoves2(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = (int)nums.size();
        int mid = nums[n/2], ans = 0;
        for(int num : nums) {
            ans += abs(num - mid);
        }
        return ans;
    }
};


2026年11月1日 星期日

ZeroJudge 解題筆記:t164.連續成績

作者:王一哲
日期:2026年11月1日


ZeroJudge 題目連結:t164.連續成績

解題想法


每組測資第一列為一個整數 $n ~(1 \leq n \leq 2 \times 10^5)$,代表有 $n$ 場比賽;第二列有 $n$ 個整數 $a_i ~(-10^9 \leq a_i \leq 10^9)$,分別代表第 $i$ 場比賽的成績變化。找出一段非空連續區間,使這段區間的成績變化總和最大,並輸出該總和。

這題是連續區間最大和問題,很適合用滑動視窗 (sliding window) 解題。假設連續區間最大和為 $imax$,目前區間和為 $curr$,兩者預設值皆為第 0 場比賽的分數變化 $arr[0]$;視窗左端點 $left = 0$。用一個 for 迴圈更新視窗右端點 $right = 0$ 到 $n-1$,先更新 $curr += arr[right]$,再用一層 while 迴圈試著移除 $arr[left] \leq 0$ 的項目,使區間加總 $curr$ 盡量變大。如果跑完內層的 while 迴圈之後,$curr < arr[right]$,則改成以 $right$ 為新的區間起點,$curr = arr[right]$,$left = right$。for 迴圈最後才更新 $imax$。跑完 for 迴圈之後輸出 $imax$。

Python 程式碼


使用時間約為 11 ms,記憶體約為 9.6 MB,通過測試。
def solve():
    import sys

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        # 讀取  n 場比賽的分數
        n = int(data[ptr])
        ptr += 1
        arr = tuple(map(int, data[ptr : ptr + n]))
        ptr += n
    
        # 用滑動視窗找連續區間加總最大值 imax
        # 目前的區間加總 curr,兩者皆預設為 arr[0]
        imax = curr = arr[0]
        left = 0
        for right in range(1, n):
            curr += arr[right]
            # 試著移除左端點小於、等於 0 的項目
            while left < right and arr[left] <= 0:
                curr -= arr[left]
                left += 1
            # 如果 curr 小於 arr[right],以 right 為新的區間起點
            if curr <= arr[right]:
                curr = arr[right]
                left = right
            imax = max(imax, curr)
        result.append(f"{imax:d}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


LeetCode 解題筆記:453. Minimum Moves to Equal Array Elements

作者:王一哲
日期:2026年11月1日


LeetCode 題目連結:453. Minimum Moves to Equal Array Elements

解題想法


中等難度題。題目給一個長度為 $n$ 的陣列 $nums$,每次操作可以將陣列中 $n-1$ 項各加上 $1$,至少要操作幾次才能夠讓陣列中每一項都一樣大?

這題考數學。如果原來的陣列中最小值為 $imin$,加總為 $isum$,假設要操作 $m$ 次,則陣列加總變成 $$ isum + m \times (n-1) = n \times (imin + m) \Rightarrow m = isum - n \times imin $$ 這題用 Python 或 C++ 都可以一行解。

Python 程式碼


Runtime: 1 ms, beats 89.24%. Memory: 20.34 MB, beats 87.24%.
class Solution:
    def minMoves(self, nums: list[int]) -> int:
        return sum(nums) - len(nums) * min(nums)


Runtime: 7 ms, beats 52.32%. Memory: 20.34 MB, beats 87.24%.
class Solution:
    def minMoves(self, nums: list[int]) -> int:
        isum, imin = 0, float('inf')
        for num in nums:
            isum += num
            if num < imin:
                imin = num
        return isum - imin * len(nums)


C++ 程式碼


Runtime: 0 ms, beats 100.00%. Memory: 31.80 MB, beats 99.75%.
class Solution {
public:
    int minMoves(vector<int>& nums) {
        return accumulate(nums.begin(), nums.end(), 0) - nums.size() * (*min_element(nums.begin(), nums.end()));
    }
};


Runtime: 0 ms, beats 100.00%. Memory: 31.90 MB, beats 97.11%.
class Solution {
public:
    int minMoves(vector<int>& nums) {
        int n = (int)nums.size(), imin = INT_MAX;
        long isum = 0;
        for(int num : nums) {
            isum += num;
            if (num < imin) imin = num;
        }
        return isum - n * imin;
    }
};


2026年10月31日 星期六

LeetCode 解題筆記:3665. Twisted Mirror Path Count

作者:王一哲
日期:2026年10月31日


LeetCode 題目連結:3665. Twisted Mirror Path Count

解題想法


中等難度題。題目給一個 $m \times n$ 的二維陣列 $grid$,其中 $0$ 代表空地,$1$ 代表鏡子。機器人從 $(0, 0)$ 出發,只能向右或向下走,終點為 $(m-1, n-1)$,保證起點、終點一定是空地。如果原來向右走,遇到鏡子時改成向下走;如果原來向下走,遇到鏡子時改成向右走。計算可能的路徑數量,並對 $mod = 10^9 + 7$ 取餘數。

這題考動態規畫,由於更新走到這一列每一格的方法數時,只會用到上一列及這一列左側格子的方法數,可以開兩個長度皆為 $n$ 的一維陣列 $right, down$,分別記錄向右走、向下走到這格的方法數,預設 $right[0] = 1, down[0] = 1$,其它項皆為 $0$。用兩層迴圈更新每一格的方法數,假設走到 $grid[i][j]$,如果 $j > 0$ 代表可以從左向右走到這格,方法數 $a = right[j-1]$,反之為 $0$;如果 $i > 0$ 代表可以從上向下走到這格,方法數 $b = down[j]$,反之為 $0$;走到這格的總方法數 $total = (a + b) % mod$。如果走到終點,直接回傳 total。如果 $grid[i][j]$ 是空地,$right[j], down[j]$ 方法數更新為 $total$。如果 $grid[i][j]$ 是鏡子,向右、向下走的方法數交換。如果跑完兩層迴圈都沒有走到終點,回傳 $0$。

Python 程式碼


Runtime: 643 ms, beats 84.62%. Memory: 34.02 MB, beats 83.76%.
class Solution:
    def uniquePaths(self, grid: List[List[int]]) -> int:
        mod = 10**9 + 7
        m, n = len(grid), len(grid[0])
        right = [0] * n  # 離開 row[j] 且向右走的方法數
        down = [0] * n  # 離開 row[j] 且向下走的方法數
        right[0] = down[0] = 1  # 起點 (0, 0) 方法數 1

        for i in range(m):
            for j in range(n):
                # 跳過起點
                if i == j == 0: continue

                # 一般狀況
                a = right[j-1] if j > 0 else 0  # 由左邊的格子走過來的方法數
                b = down[j] if i > 0 else 0  # 由上面的格子走過來的方法數

                if grid[i][j] == 0:  # 遇到空地
                    total = (a + b) % mod  # 走到 grid[i][j] 的方法數
                    if i == m-1 and j == n-1:  # 走到終點,直接回傳方法數
                        return total
                    # 更新 right 及 down
                    right[j] = down[j] = total
                else:  # 遇到鏡子,向右走、向下走方法數互換
                    right[j], down[j] = b, a
        
        # 如果沒有觸發第 10 行的條件,走不到終點,回傳 0
        return 0


C++ 程式碼


Runtime: 52 ms, beats 90.83%. Memory: 194.13 MB, beats 87.50%.
class Solution {
public:
    int uniquePaths(vector<vector<int>>& grid) {
        const int mod = 1000000007;
        int m = grid.size(), n = grid[0].size();
        vector<int> right (n, 0), down (n, 0);
        right[0] = 1;
        down[0] = 1;

        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                // 跳過起點
                if (i == 0 && j == 0) continue;

                // 一般狀況,從左向右走到這格方法數 a,從上向下走到這格方法數 b
                int a = (j > 0 ? right[j-1] : 0);
                int b = (i > 0 ? down[j] : 0);
                int total = (a + b) % mod;

                // 走到終點,直接回傳 total
                if (i == m-1 && j == n-1) return total;

                // 如果這格是空地,right[j], down[j] 方法數更新為 total
                if (grid[i][j] == 0) {
                    right[j] = total;
                    down[j] = total;
                } else {  // 鏡子,向右、向下走的方法數交換
                    right[j] = b;
                    down[j] = a;
                }
            }
        }
        // 如果沒有觸發第 20 行的條件,走不到終點,回傳 0
        return 0;
    }
};


C 語言程式碼


Runtime: 49 ms, beats 100.00%. Memory: 47.66 MB, beats 71.43%.
int uniquePaths(int** grid, int gridSize, int* gridColSize) {
    const int mod = 1000000007;
    int m = gridSize, n = *gridColSize;
    int right[501] = {0}, down[501] = {0};
    right[0] = 1;
    down[0] = 1;

    for(int i = 0; i < m; i++) {
        for(int j = 0; j < n; j++) {
            // 跳過起點
            if (i == 0 && j == 0) continue;

            // 一般狀況,從左向右走到這格方法數 a,從上向下走到這格方法數 b
            int a = (j > 0 ? right[j-1] : 0);
            int b = (i > 0 ? down[j] : 0);
            int total = (a + b) % mod;

            // 走到終點,直接回傳 total
            if (i == m-1 && j == n-1) return total;

            // 如果這格是空地,right[j], down[j] 方法數更新為 total
            if (grid[i][j] == 0) {
                right[j] = total;
                down[j] = total;
            } else {  // 鏡子,向右、向下走的方法數交換
                right[j] = b;
                down[j] = a;
            }
        }
    }
    // 如果沒有觸發第 20 行的條件,走不到終點,回傳 0
    return 0;
}


ZeroJudge 解題筆記:t163.挑選紀念品

作者:王一哲
日期:2026年10月31日


ZeroJudge 題目連結:t163.挑選紀念品

解題想法


每組測資第一列為兩個整數 $n ~(1 \leq n \leq 40), w ~(0 \leq w \leq 10^9)$,代表有 $n$ 件紀念品,背包最多可以裝到總重量 $w$;第二列有 $n$ 個正整數 $a_i ~(1 \leq a_i \leq 10^9)$,分別代表第 $i$ 個紀念品的重量。每件紀念品最多只能選一次,在不超重的條件下共有幾種挑選方式。

這題看起來是標準的 0/1 背包問題,但是 $w$ 的範圍太大,如果用一個長度為 $w+1$ 的一維陣列 $dp$ 記錄挑選方法數,這樣會超出記憶體上限而且速度很慢,需要用折半枚舉才能 AC。不過這題的記憶體上限為 64 MB,Python 就算用折半枚舉還是無法 AC,依然會超出記憶體上限。

Python 程式碼


只能通過 60% 的測資。
def solve():
    import sys
    from bisect import bisect_right
    
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        w = int(data[ptr + 1])
        ptr += 2
        # 讀取物品重量
        items = [int(x) for x in data[ptr : ptr + n]]
        ptr += n

        # 折半切分
        mid = n // 2
        left_items = items[: mid]
        right_items = items[mid :]
        
        # 自訂函式,窮舉左、右兩半所有的重量組合
        def get_subset_sum(arr):
            sums = [0]
            for x in arr:
                sums += [s + x for s in sums]
            return sums

        # 呼叫 get_subset_sum,窮舉左、右兩半子集合
        left_sums = get_subset_sum(left_items)
        right_sums = get_subset_sum(right_items)
        
        # 右半部排序,用於二分搜
        right_sums.sort()
            
        # 走訪左半部,用二分搜找右半部合法的數量
        ans = 0
        for isum in left_sums:
            if isum > w: continue  # 左半部就超重,略過
            # 從右半部找大於 (w - isum) 的第一個位置
            ans += bisect_right(right_sums, w - isum)
        
        sys.stdout.write(f"{ans:d}\n")

if __name__ == "__main__":
    solve()


2026年10月30日 星期五

LeetCode 解題筆記:3970. Shortest Path With At Most K Consecutive Identical Characters

作者:王一哲
日期:2026年10月30日


LeetCode 題目連結:3970. Shortest Path With At Most K Consecutive Identical Characters

解題想法


中等難度題。題目給一個整數 $n ~(1 \leq n \leq 500000)$,代表有 $n$ 個節點,編號為 $0$ 到 $n-1$。一個二維陣列 $edges$,其中 $edges[i] = [u_i, v_i, w_i]$,代表有一條可以從 $u_i$ 走要 $v_i$ 且權重為 $w_i$ 的邊。再給一個長度為 $n$ 的字串 $labels$,其中 $labels[i]$ 代表節點 $i$ 的標籤。最後給一個整數 $k$,代表路徑上同一個字母最多只能連續出現 $k$ 次。找出所有由節點 $0$ 走到 $n-1$,同一個字母最多能連續出現 $k$ 次的路徑之中,路徑權重加總的最小值。如果沒有這樣的路徑則回傳 $-1$。

首先排除特例,$n$ 等於 $0$,不需要走,回傳 $0$。接下來處理一般狀況。先用接鄰矩陣 $adj$ 存有向圖,記錄從節點 $u$ 可以走到的節點及權重。解題重點在於如何用 Dijkstra 演算法,找走到各節點、各種末端連續相同字母數量對應的最低權重。定義一個大小為 $n \times (k+1)$ 的二維陣列 $dist$,$dist[i][j]$ 代表走到節點 $i$、末端有 $j$ 個連續相同字母對應的最低權重,預設值為超過題目上限的值。用最小優先佇列 $pq$ 儲存待走訪的狀態,內容為 {權重, 節點編號, 字母數量},權重低的放最上面。寫一個 while 迴圈,若 $pq$ 有資料繼續執行,每次先取出權重 $w$、節點 $u$、字母數量 $cnt$,再檢查 $u$ 可以走到的節點 $v$ 及權重 $d$,如果 $labels[u] == labels[v]$,新的數量 $nxt = cnt + 1$; 反之 $nxt = 1$。如果 $nxt \leq k$ 而且 $w + d < dist[v][nxt]$,找到新的最低權重路徑,更新 $dist[v][nxt]$,將 ${w + d, v, nxt}$ 加入 $pq$。全部跑完之後,答案 $ans$ 為 $dist[n-1][1]$ 到 $dist[n-1][k]$ 之中的最小值。如果 $ans$ 等於預設的極大值,代表找不到路徑,回傳 $-1$。

Python 程式碼


Runtime: 554 ms, beats 13.25%. Memory: 67.95 MB, beats 16.87%.
class Solution:
    def shortestPath(self, n: int, edges: List[List[int]], labels: str, k: int) -> int:
        # 特例,n = 1,不需要走,回傳 0
        if n == 1: return 0
        
        # 一般狀況
        adj = [[] for _ in range(n)]  # u: [(v1, w1), (v2, w2), ...]
        for u, v, w in edges:
            adj[u].append((v, w))
        
        # dist[i][j] 代表走到節點 i、最後一個字母連續出現 j 次的最低權重
        dist = [[float('inf')] * (k + 1) for _ in range(n)]
        dist[0][1] = 0  # 起點,次數1,權重 0

        # Dijkstra 演算法,找符合條件的最低權重路徑
        pq = [(0, 0, 1)]  # 最小優先佇列,(權重,節點編號,最後一個字母連續出現次數)
        while pq:
            w, u, cnt = heapq.heappop(pq)
            # 如果目前 dist 之中的權重較低,跑下一組
            if w > dist[u][cnt]: continue
            
            # 測試 u 可以走到的節點 v
            for v, d in adj[u]:  # u 走到 v,權重 d
                # 相同字母,次數加 1;不同字母,次數重設為 1
                nxt = cnt + 1 if labels[u] == labels[v] else 1
                
                # 次數合法而且走到 v 的路徑權較低
                if nxt <= k and w + d < dist[v][nxt]:                    
                    dist[v][nxt] = w + d
                    heapq.heappush(pq, (w + d, v, nxt))
        
        ans = min(dist[n-1])
        
        return ans if ans < float('inf') else -1


C++ 程式碼


Runtime: 348 ms, beats 27.91%. Memory: 294.99 MB, beats 46.51%.
class Solution {
public:
    int shortestPath(int n, vector<vector<int>>& edges, string labels, int k) {
        // 特例,n 等於 0,不需要走
        if (n == 0) return 0;

        // 一般狀況,先用接鄰矩陣存有向圖
        vector<vector<pair<int, int>>> adj (n);
        for(auto edge : edges) {
            int u = edge[0], v = edge[1], w = edge[2];
            adj[u].push_back({v, w});
        }

        // Dijkstra 演算法,找走到各節點、各種末端連續相同字母數量對應的最低權重
        vector<vector<int>> dist (n, vector<int> (k+1, 1000000000));
        dist[0][1] = 0;  // 起點 
        
        // 最小優先佇列,{權重, 節點編號, 字母數量}
        priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<tuple<int, int, int>>> pq;
        pq.push({0, 0, 1});  // 權重 0,起點 0,數量 1
        while(!pq.empty()) {
            auto [w, u, cnt] = pq.top();
            pq.pop();
            for(auto it : adj[u]) {
                int v = it.first, d = it.second;
                int nxt = (labels[u] == labels[v] ? cnt + 1 : 1);
                if (nxt <= k && w + d < dist[v][nxt]) {
                    dist[v][nxt] = w + d;
                    pq.push({w + d, v, nxt});
                }
            }
        }

        int ans = *min_element(dist[n-1].begin(), dist[n-1].end());
        return (ans < 1000000000 ? ans : -1);
    }
};


ZeroJudge 解題筆記:t161.硬幣湊零

作者:王一哲
日期:2026年10月30日


ZeroJudge 題目連結:t161.硬幣湊零

解題想法


每組測資第一列為兩個整數 $d ~(1 \leq d \leq 6), x ~(0 \leq x \leq 10^5)$,代表有 $d$ 種面額的硬幣,目標金額為 $x$;第二列有 $d$ 個正整數 $c_i ~(1 \leq c_i \leq 1000)$,分別代表第 $i$ 種硬幣的面額。如果要湊出 $x$ 最少需要幾枚硬幣。

這題是標準的無限背包問題。定義最大數量為目標值加 $1$,$maxn = target + 1$,因為這題即使全部用 1 元硬幣湊出目標值 $target$ 也只需要 $target$ 枚硬幣。開一個長度為 $maxn$、預設值皆為 $maxn$ 的一維陣列 $dp$,$dp[i]$ 代表總金額 $i$ 需要的最少硬幣數量。初始值 $dp[0] = 0$,因為金額 0 不需要硬幣。再用兩層 for 迴圈更新 $dp$,外層 for 迴圈跑硬幣面額,內層 for 迴圈由小到大跑總金額。如果最後 $dp[target] == maxn$,代表無法湊出 $target$,輸出 $-1$;反之輸出 $dp[target]$。測試後發現這題是單筆測資,不過寫成處理多筆測資的版本也能 AC。

Python 程式碼


使用時間約為 0.1 s,記憶體約為 13.4 MB,通過測試。
def solve():
    import sys

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        target = int(data[ptr + 1])
        ptr += 2
        coins = tuple(map(int, data[ptr : ptr + n]))
        ptr += n

        # 無限背包
        maxn = target + 1
        dp = [maxn] * maxn
        dp[0] = 0
        for coin in coins:
            for j in range(coin, target + 1):
                if dp[j - coin] < maxn:
                   dp[j] = min(dp[j], dp[j - coin] + 1)
        result.append(f"{dp[target]:d}\n" if dp[target] < maxn else "-1\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年10月29日 星期四

ZeroJudge 解題筆記:p926.礦業調度 (Mining)

作者:王一哲
日期:2026年10月29日


ZeroJudge 題目連結:p926.礦業調度 (Mining)

題目 pdf 檔連結:礦業調度 (Mining)

解題想法


每組測資第一列為兩個整數 $n ~(1 \leq n \leq 10^5), m ~(1 \leq m \leq 100)$,代表有 $n$ 座礦脈、共開採 $m$ 天;第二列有 $n$ 個正整數 $a_i ~(1 \leq a_i \leq 10^6)$,分別代表第 $i$ 座礦脈一開始每天可以開採的金礦。某一座礦脈開採一天之後,隔天可以開採的量會減 $1$。運輸金礦的成本與礦脈索引值有關,第 $i$ 座礦脈的運輸成本為 $i-1$。求這 $m$ 天的最大收益。

這題很適合用最大優先佇列解題。開一個最大優先佇列 $pq$,假設第 $i$ 座礦脈一開始每天可以開採的金礦減掉運輸成本之後大於 $0$,加入 pq。設定答案 $ans = 0$。接下來跑 while 迴圈,當開採天數 $m$ 還沒有歸零而且 $pq$ 還有資料時繼續執行,取出 $pq$ 之中的最大值 $v$,答案 $ans += v$,如果 $v > 1$,將 $v-1$ 加入 $pq$。跑完 while 迴圈之後印出 $ans$。

Python 程式碼


使用時間約為 57 ms,記憶體約為 18.8 MB,通過測試。
def solve():
    import sys, heapq
    
    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])  # n 座礦脈
        m = int(data[ptr + 1])  # 開採 m 天
        ptr += 2
        
        # 用最大優先佇列儲存各個礦脈目前的收益 = 産量 - 成本
        pq = []
        for i in range(1, n + 1):
            v = int(data[ptr]) - (i - 1)
            ptr += 1
            if v > 0:  # 收益大於 0 才放進來
                heapq.heappush(pq, -v)
        
        # 最多開採 m 天且 pq 要有資料
        ans = 0  # 答案
        while m > 0 and pq:
            m -= 1
            v = -heapq.heappop(pq)
            ans += v
            if v > 1:
                heapq.heappush(pq, -(v - 1))
        
        result.append(f"{ans:d}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年10月28日 星期三

ZeroJudge 解題筆記:p925.鬆餅 (Waffle)

作者:王一哲
日期:2026年10月28日


ZeroJudge 題目連結:p925.鬆餅 (Waffle)

題目 pdf 檔連結:鬆餅 (Waffle)

解題想法


每組測資第一列為兩個整數,代表兩種口味鬆餅分別需要的烘烤時間 $t_i ~(1 \leq t_i \leq 20)$;第二列有一個正整數 $m$,代表有 $m$ 張訂單;接下來 $m$ 列每列有一個正整數,代表這張訂單的鬆餅口味。只有兩台鬆餅機,只計算烘烤時間,而且要按照訂單順序製作鬆餅,求完成所有訂單需要的最短時間。

這題用貪心法解題,假設兩台鬆餅機使用的時間分別為 $a, b$,讀取到一張新的訂單時由當時最快可用的鬆餅機製作,將這台鬆餅機使用的時間加上這個口味鬆餅的烘烤時間。完成所有的訂單之後,答案為 $a, b$ 較大的值。

題目 pdf 檔裡面說是多筆測資,但是 ZeroJudge 的測資似乎是單筆測資,如果用多筆測資的寫法反而會在測資 #4 遇到 Index out of range。如果加上檢查剩餘資料長度的防呆裝置,反而會變成多輸出一個題目沒有要求的文字 1。改成處理單筆測資的寫法才能 AC。

Python 程式碼


在測資 #4 遇到 Index out of range。
def solve():
    import sys

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        # 讀取測資
        time = [0] + list(map(int, data[ptr : ptr + 2]))
        ptr += 2
        m = int(data[ptr])
        ptr += 1
        a, b = 0, 0
        for _ in range(m):
            idx = int(data[ptr])
            ptr += 1
            if a <= b:
                a += time[idx]
            else:
                b += time[idx]
        
        result.append(f"{max(a, b):d}\n")
            
    # 一次輸出所有答案
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


LeetCode 解題筆記:3882. Minimum XOR Path in a Grid

作者:王一哲
日期:2026年10月28日


LeetCode 題目連結:3882. Minimum XOR Path in a Grid

解題想法


中等難度題。題目給一個 $m$ 列、$n$ 欄的陣列 $grid$,範圍為 $1 \leq m, n \leq 1000, ~ m \times n \leq 1000, ~ 0 \leq grid[i][j] \leq 1023$。出發點為左上角 $(0, 0)$,終點為右下角 $(m-1, n-1)$,只能向右或向下走,找出路徑上所有格子取 XOR 的最小值。

這題考動態規畫,可以建一個 2 維陣列 $dp$,$dp[i][j]$ 代表走到 $grid[i][j]$ 可能的路徑 XOR 值。因為取 XOR 的結果不可能大於原來的值,因此答案只在 $0$ 到 $1023$ 之間,Python 可以用 set 或是 bitmask 記錄可能的值,C++ 則可以用 bitset。由於更新 $dp$ 時只需要用到前一列的狀態,可以只開長度為 $n$ 的一維陣列記錄狀態,更新走到 $grid[i][j]$ 的狀態時,再建一個 bitset $nxt$ 記錄新的狀態,舊的狀態記錄在 $pre$,更新完 $nxt$ 之後再將資料存到 $dp[j]$。更新完所有的格子之後,答案在 $dp[n-1]$ 之中為 true 的最低位數。

Python 程式碼


bitmask,比想像中慢。Runtime: 3313 ms, beats 31.67%. Memory: 19.29 MB, beats 100.00%.
class Solution:
    def minCost(self, grid: list[list[int]]) -> int:
        m, n = len(grid), len(grid[0])
        # 0 ~ 1023,第 i 位為 1 代表存在加總為 i 的路徑
        dp = [0] * n
        
        for i in range(m):
            for j in range(n):
                if i == 0 and j == 0:  # 起點
                    dp[0] = (1 << grid[0][0])
                else:  # 其它位置
                    pre = 0
                    if i > 0:  pre |= dp[j]  # 從上往下走
                    if j > 0:  pre |= dp[j-1]  # 從左往右走
                    
                    # 用 lowbit 從最後一個 1 往前更新
                    nxt = 0  # 新的狀態
                    val = grid[i][j]
                    while pre > 0:
                        lowbit = pre & -pre
                        k = lowbit.bit_length() - 1  # 第 k 位為 1
                        nxt |= (1 << (k ^ val))
                        pre &= pre - 1  # 清除最後的 1
                    
                        
                    dp[j] = nxt
                  
        # 在終點 n-1 由小到大找第 1 個 1 的位置 
        return (dp[n-1] & -dp[n-1]).bit_length() - 1


2026年10月27日 星期二

LeetCode 解題筆記:3963. Create Grid With Exactly One Path

作者:王一哲
日期:2026年10月27日


LeetCode 題目連結:3963. Create Grid With Exactly One Path

解題想法


簡單題。題目給兩個整數 $m, n ~(1 \leq m, n \leq 25)$,要建立一個 $m$ 列、$n$ 欄的陣列,其中只有代表空格的 . 及代表障礙物的 #,出發點為 $(0, 0)$,終點為 $(m-1, n-1)$,只能向右或向下走,回傳的陣列只能有一條合法的路徑。

這題最簡單的走法是先向右走到 $(0, n-1)$ 再向下走到 $(m-1, n-1)$,或是先向下走到 $(m-1, 0)$ 再向右走到 $(m-1, n-1)$。這題的回傳格式會依據語言而變,Python 是 $m$ 列、長度 $n$ 的字串串列,C++ 是 $m$ 列、長度 $n$ 的字串陣列,C 語言是 $m$ 列、$n$ 欄的字元陣列。

Python 程式碼


Runtime: 2 ms, beats 47.13%. Memory: 19.05 MB, beats 96.31%.
class Solution:
    def createGrid(self, m: int, n: int) -> list[str]:
        return ['.' * n] + ['#' * (n-1) + '.' for _ in range(m-1)]


Runtime: 1 ms, beats 52.46%. Memory: 18.99 MB, beats 99.59%.
class Solution:
    def createGrid(self, m: int, n: int) -> list[str]:
        return ['.' + '#' * (n-1) for _ in range(m-1)] + ['.' * n]


C++ 程式碼


Runtime: 1 ms, beats 57.19%. Memory: 11.10 MB, beats 74.25%.
class Solution {
public:
    vector<string> createGrid(int m, int n) {
        vector<string> grid (m, string (n, '#'));
        for(int j = 0; j < n; j++) {
            grid[0][j] = '.';
        }
        for(int i = 1; i < m; i++) {
            grid[i][n-1] = '.';
        }
        return grid;
    }
};


Runtime: 2 ms, beats 45.48%. Memory: 11.11 MB, beats 57.19%.
class Solution {
public:
    vector<string> createGrid(int m, int n) {
        vector<string> grid (m, string (n, '#'));
        for(int i = 0; i < m; i++) {
            grid[i][0] = '.';
        }
        for(int j = 1; j < n; j++) {
            grid[m-1][j] = '.';
        }        
        return grid;
    }
};


ZeroJudge 解題筆記:p924.水晶共鳴 (Crystal)

作者:王一哲
日期:2026年10月27日


ZeroJudge 題目連結:p924.水晶共鳴 (Crystal)

題目 pdf 檔連結:水晶共鳴 (Crystal)

解題想法


每組測資第一列為一個整數 $n$,代表共有 $n$ 個水晶;第二列有 $n$ 個數字,分別第 $i$ 個水晶的屬性。如果相鄰且相同屬性的水晶會共鳴,融合成一個能量團,融合的水晶個數為能量團的大小,求這些水晶由左到右組成的能量團大小。這題可以在記錄水晶屬性的陣列 $crystal$ 最後面加一個不在測資範圍內的數字,例如 $0$,用來強制結算最後一組能量團。假設目前的水晶屬性 $curr = crystal[0]$,目前的能量團大小 $isum = 1$;用一個 for 迴圈跑 $crystal[1]$ 到 $crystal[n]$,如果 $crystal[i]$ 等於 $curr$,相同屬性,$isum + 1$;反之,不同屬性,結算前一組能量團,$isum$ 加到答案 $ans$ 最後面,重置 $isum = 1$、$curr = crystal[i]$。

Python 程式碼


使用時間約為 54 ms,記憶體約為 22.4 MB,通過測試。
def solve():
    import sys

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        # 各水晶屬性,結尾加一個 0,強制結算最後一組
        crystal = list(map(int, data[ptr : ptr + n])) + [0]
        ptr += n
        ans = []  # 答案
        curr = crystal[0]  # 目前的水晶屬性
        isum = 1  # 目前的能量團量值
        for v in crystal[1:]:  # 依序讀取水晶屬性
            if v == curr:  # 同屬性,能量團量值加 1
                isum += 1
            else:  # 不同屬性,結算前一組
                ans.append(isum)
                isum = 1
                curr = v
        result.append(" ".join(map(str, ans)) + "\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年10月26日 星期一

LeetCode 解題筆記:3619. Count Islands With Total Value Divisible by K

作者:王一哲
日期:2026年10月26日


LeetCode 題目連結:3619. Count Islands With Total Value Divisible by K

解題想法


中等難度題。題目給一個二維陣列 $grid$,其中正整數代表陸地,0 代表海洋,地圖邊緣視為海洋。陸地可以與右、下、左、上相鄰的陸地相連,如果周圍被海洋環繞一圈,這片相連的陸地是一座島嶼。計算 $grid$ 之中有幾座島嶼的數值加總可以被 $k$ 整除。

這題是標準的 BFS。用雙層 for 迴圈掃過 $grid$ 每一格,如果遇到正整,從這格開始 BFS 計算加總 $val$,計算的過程中同時對 $k$ 取餘數。將走過的陸地都標示為 0,直接修改 $grid$ 的內容即可,節省記憶體。BFS 結束之後,如果 $val$ 等於 0,答案 $ans$ 加 1。

Python 程式碼


Runtime: 270 ms, beats 90.00%. Memory: 30.06 MB, beats 93.79%.
class Solution:
    def countIslands(self, grid: List[List[int]], k: int) -> int:
        m, n, ans = len(grid), len(grid[0]), 0  # 地圖 m*n,答案
        # 掃過每一格,如果這格不等於 0,BFS 走完相連的格子
        for i in range(m):
            for j in range(n):
                if grid[i][j] != 0:
                    val = grid[i][j] % k
                    que = deque([(i, j)])
                    grid[i][j] = 0
                    while que:
                        r, c = que.popleft()
                        for dr, dc in ((0, 1), (1, 0), (0, -1), (-1, 0)):
                            nr, nc = r + dr, c + dc
                            if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] != 0:
                                val = (val + grid[nr][nc]) % k
                                grid[nr][nc] = 0
                                que.append((nr, nc))
                    
                    if val == 0: ans += 1  # 符合條件,答案加 1

        return ans


ZeroJudge 解題筆記:p923.湯圓 (Tangyuan)

作者:王一哲
日期:2026年10月26日


ZeroJudge 題目連結:p923.湯圓 (Tangyuan)

題目 pdf 檔連結:湯圓 (Tangyuan)

解題想法


每組測資第一列為一個整數 $n$,代表共有 $n$ 個人;接下來 $n$ 列每列有 $2$ 個數字 $x, y$,分別代表這個人想吃的芝麻和花生湯圓數量。如果湯圓一盒 $10$ 顆,兩種口味的湯圓至少要各買幾盒?各會剩下幾顆?這題可以用兩個變數 $sesame, peanut$,分別代表芝麻、花生湯圓的數量。用一個 for 迴圈讀取每個人要吃的數量並更新 $sesame, peanut$。盒數為數量對 $10$ 取整數除法,如果有餘數再加 $1$ 盒。剩下的數量可以用 $盒數 \times 10 - 要吃的數量$。

Python 程式碼


使用時間約為 14 ms,記憶體約為 9.6 MB,通過測試。
def solve():
    import sys

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        # 讀取測資
        n = int(data[ptr])
        ptr += 1
        sesame, peanut = 0, 0  # 芝麻、花生湯圓數量
        for _ in range(n):
            x = int(data[ptr])
            y = int(data[ptr + 1])
            ptr += 2
            sesame += x
            peanut += y
        
        rem1 = sesame % 10
        num1 = sesame // 10 + (rem1 > 0)
        rem2 = peanut % 10
        num2 = peanut // 10 + (rem2 > 0)
        result.append(f"{num1:d} {10 * num1 - sesame:d}\n{num2:d} {10 * num2 - peanut:d}\n")
    
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年10月25日 星期日

ZeroJudge 解題筆記:p922.選舉 (Election)

作者:王一哲
日期:2026年10月25日


LeetCode 題目連結:3619. Count Islands With Total Value Divisible by K

解題想法


中等難度題。題目給一個二維陣列 $grid$,其中正整數代表陸地,0 代表海洋,地圖邊緣視為海洋。陸地可以與右、下、左、上相鄰的陸地相連,如果周圍被海洋環繞一圈,這片相連的陸地是一座島嶼。計算 $grid$ 之中有幾座島嶼的數值加總可以被 $k$ 整除。

這題是標準的 BFS。用雙層 for 迴圈掃過 $grid$ 每一格,如果遇到正整,從這格開始 BFS 計算加總 $val$,計算的過程中同時對 $k$ 取餘數。將走過的陸地都標示為 0,直接修改 $grid$ 的內容即可,節省記憶體。BFS 結束之後,如果 $val$ 等於 0,答案 $ans$ 加 1。

Python 程式碼


Runtime: 270 ms, beats 90.00%. Memory: 30.06 MB, beats 93.79%.
class Solution:
    def countIslands(self, grid: List[List[int]], k: int) -> int:
        m, n, ans = len(grid), len(grid[0]), 0  # 地圖 m*n,答案
        # 掃過每一格,如果這格不等於 0,BFS 走完相連的格子
        for i in range(m):
            for j in range(n):
                if grid[i][j] != 0:
                    val = grid[i][j] % k
                    que = deque([(i, j)])
                    grid[i][j] = 0
                    while que:
                        r, c = que.popleft()
                        for dr, dc in ((0, 1), (1, 0), (0, -1), (-1, 0)):
                            nr, nc = r + dr, c + dc
                            if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] != 0:
                                val = (val + grid[nr][nc]) % k
                                grid[nr][nc] = 0
                                que.append((nr, nc))
                    
                    if val == 0: ans += 1  # 符合條件,答案加 1

        return ans


LeetCode 解題筆記:1254. Number of Closed Islands

作者:王一哲
日期:2026年10月25日


LeetCode 題目連結:1254. Number of Closed Islands

解題想法


中等難度題。題目給一個二維陣列 $grid$,其中 0 代表陸地,1 代表海洋。陸地可以與右、下、左、上相鄰的陸地相連,如果周圍被海洋環繞一圈,這片相連的陸地是一座島嶼。計算 $grid$ 之中有幾座島嶼。

這題是標準的 BFS。用雙層 for 迴圈掃過 $grid$ 每一格,如果遇到 0,可能是島嶼,用 is_island 標記是否為島嶼,預設為 True。接下來從這格開始 BFS,將走過的陸地都標示為 2,直接修改 $grid$ 的內容即可,節省記憶體。如果走的過程中出界,不是島嶼,is_land 改成 False。BFS 結束之後,如果 is_land 為 True,答案 $cnt + 1$。

Python 程式碼


Runtime: 12 ms, beats 87.91%. Memory: 19.76 MB, beats 75.00%.
from collections import deque

class Solution:
    def closedIsland(self, grid: list[list[int]]) -> int:
        m, n = len(grid), len(grid[0])   # 地圖尺寸 m*n     
        cnt = 0  # 島嶼數量

        # 掃過 grid 每一格,遇到 0 用 BFS 走完相連的 0,走過的格子標記成 2
        # 如果過程中會出界,不是島嶼
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 0:
                    is_island = True
                    que = deque([(i, j)])
                    grid[i][j] = 2
                    while que:
                        r, c = que.popleft()
                        for dr, dc in ((0, 1), (1, 0), (0, -1), (-1, 0)):
                            nr, nc = r + dr, c + dc
                            if nr < 0 or nr >= m or nc < 0 or nc >= n:
                                is_island = False
                            elif grid[nr][nc] == 0:
                                que.append((nr, nc))
                                grid[nr][nc] = 2
                    if is_island: cnt += 1
        return cnt


2026年10月24日 星期六

LeetCode 解題筆記:2409. Count Days Spent Together

作者:王一哲
日期:2026年10月24日


LeetCode 題目連結:2409. Count Days Spent Together

解題想法


簡單題。題目給 4 個長度皆為 5 的字串,格式為 MM-DD,MM 代表月,DD 代表日。4 個字串分別是代表 Alice 抵達的日期 arriveAlice,Alice 離開的日期 leaveAlice,Bob 抵達的日期 arriveBob,Bob 離開的日期 leaveBob。計算 Alice 與 Bob 一起待在羅馬的天數。假設是平年,2 月只有 28 天。

由於日期格式很困定,可以用 Python 的字串切片或是 C++ 的 substr,將字串中的月、日分開,轉成整數。如果用 C 語言比較麻煩,我是用索引值取出字元再減去 '0' 換成對應的整數,每個位數都要分開算。在計算天數時,我將日期全部轉成由1月1日開始的累計天數。為了計算月份對應的天數,我開一個長度為 13 的陣列 $month = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)$,記錄經過完整的月份時共經過幾天。答案為兩個人離開日期最小值 - 兩個人離開日期最大值,如果答案是負的則回傳 0。

Python 程式碼


Runtime: 0 ms, beats 100.00%. Memory: 19.29 MB, beats 79.30%.
class Solution:
    def countDaysTogether(self, arriveAlice: str, leaveAlice: str, arriveBob: str, leaveBob: str) -> int:
        month = (0, 31, 59, 90, 120, 151, 181,
                 212, 243, 273, 304, 334, 365)
        arriveAliceDays = month[int(arriveAlice[:2]) - 1] + int(arriveAlice[3:])
        leaveAliceDays = month[int(leaveAlice[:2]) - 1] + int(leaveAlice[3:])
        arriveBobDays = month[int(arriveBob[:2]) - 1] + int(arriveBob[3:])
        leaveBobDays = month[int(leaveBob[:2]) - 1] + int(leaveBob[3:])
        ans = min(leaveAliceDays, leaveBobDays) - max(arriveAliceDays, arriveBobDays) + 1
        return ans if ans > 0 else 0


C++ 程式碼


Runtime: 0 ms, beats 100.00%. Memory: 8.05 MB, beats 38.18%.
class Solution {
public:
    int countDaysTogether(string arriveAlice, string leaveAlice, string arriveBob, string leaveBob) {
        int month[13] = 
            {0, 31, 59, 90, 120, 151, 181,
             212, 243, 273, 304, 334, 365};
        int arriveAliceDays = month[stoi(arriveAlice.substr(0, 2)) - 1] + stoi(arriveAlice.substr(3));
        int leaveAliceDays = month[stoi(leaveAlice.substr(0, 2)) - 1] + stoi(leaveAlice.substr(3));
        int arriveBobDays = month[stoi(arriveBob.substr(0, 2)) - 1] + stoi(arriveBob.substr(3));
        int leaveBobDays = month[stoi(leaveBob.substr(0, 2)) - 1] + stoi(leaveBob.substr(3));
        int ans = min(leaveAliceDays, leaveBobDays) - max(arriveAliceDays, arriveBobDays) + 1;
        return (ans > 0 ? ans : 0);
    }
};