↓快轉到主要內容
  1. LeetCode/

LeetCode 2603: Collect Coins in a Tree

·2 分鐘· ·
LeetCode Hard Tree Graph Topological-Sort
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: hard 第一次嘗試:2026-06-26

  • 提交紀錄:Wrong Answer ×2 → Accepted → Wrong Answer(重交舊版)→ Accepted

解題思路
#

可以從任意點出發,每到一個點就能收集距離 2 以內的硬幣,最後要回到起點。

關鍵是:最後走過的路徑會是一棵子樹,而走完一棵子樹再回到起點,每條邊剛好走兩次。 所以答案是「必須走的邊數 × 2」。

哪些邊必須走?用兩次剪葉子找出來:

  1. 剪掉沒有硬幣的葉子,而且要連鎖:剪掉之後,鄰居如果也變成沒有硬幣的葉子,就繼續剪。剪完之後,剩下的每片葉子都有硬幣。
  2. 再固定剪兩層葉子:站在距離葉子 2 步的位置,就能收到那片葉子的硬幣,所以最外面兩層不用真的走過去。

剩下的邊數乘以 2 就是答案。

我的嘗試
#

第一版:continue 讓連鎖剪枝失效
#

while leaves:
    leaf = leaves.popleft()
    coins[leaf] = -1
    if coins[leaf]:          # 剛設成 -1,永遠是 True
        continue
    ...

先把 coins[leaf] 設成 -1,下一行又檢查它,-1 是 truthy,所以每片葉子都直接 continue,鄰居的 degree 從來沒被扣,只剪掉了第一層。

另外還留了一行 print(neighbor, degree) 除錯輸出。

第二版:把有硬幣的鄰居也剪掉
#

拿掉 continue 之後:

if degree[neighbor] == 1:
    leaves.append(neighbor)      # 沒有檢查鄰居有沒有硬幣

鄰居變成葉子時,不管它有沒有硬幣都被放進 queue 剪掉,有硬幣的節點被誤刪。條件要改成 degree[neighbor] == 1 and coins[neighbor] == 0。

後來又不小心重交了一次舊版,所以中間多了一次 WA。

正確解法
#

from collections import deque

class Solution:
    def collectTheCoins(self, coins: List[int], edges: List[List[int]]) -> int:
        n = len(coins)
        degree = [0] * n
        adj = [[] for _ in range(n)]
        for u, v in edges:
            adj[u].append(v)
            adj[v].append(u)
            degree[u] += 1
            degree[v] += 1

        # 第一階段:連鎖剪掉沒有硬幣的葉子
        leaves = deque(i for i in range(n) if degree[i] == 1 and coins[i] == 0)
        while leaves:
            leaf = leaves.popleft()
            coins[leaf] = -1                     # 標記為已刪除
            for nei in adj[leaf]:
                degree[nei] -= 1
                if degree[nei] == 1 and coins[nei] == 0:
                    leaves.append(nei)

        # 第二階段:在剩下的樹上固定剪兩層葉子
        degree = [0] * n
        adj = [[] for _ in range(n)]
        for u, v in edges:
            if coins[u] == -1 or coins[v] == -1:
                continue
            adj[u].append(v)
            adj[v].append(u)
            degree[u] += 1
            degree[v] += 1

        leaves = deque(i for i in range(n) if degree[i] == 1)
        while leaves:
            leaf = leaves.popleft()
            coins[leaf] = -1                     # 第一層
            for nei in adj[leaf]:
                degree[nei] -= 1
                if degree[nei] == 1:
                    coins[nei] = -1              # 變成葉子的就是第二層,不再往內

        # 第三階段:數剩下的邊
        remaining = sum(1 for u, v in edges if coins[u] != -1 and coins[v] != -1)
        return remaining * 2

第二階段不把第二層放回 queue,所以剛好只剪兩層。

複雜度
#

時間 O(n),空間 O(n)。

要特別避免的錯誤
#

  • 標記之後馬上檢查同一個值,讓整段邏輯失效。
  • 連鎖剪枝時忘記檢查鄰居有沒有硬幣。
  • 第二層剪完還繼續往內剪:只能剪兩層。
  • 提交前沒有確認編輯器裡是不是最新版本。

面試口說整理
#

收集完所有硬幣再回到起點,走過的路徑是一棵子樹,每條邊剛好走兩次,所以答案是必須走的邊數乘以 2。我先用類似拓撲排序的方式連鎖剪掉沒有硬幣的葉子,讓剩下的葉子都有硬幣;因為能收集距離 2 以內的硬幣,再固定剪兩層葉子。剩下的邊數乘以 2 就是答案,時間 O(n)。

相關文章

LeetCode 269: Alien Dictionary
·2 分鐘
LeetCode Hard Graph Topological-Sort
LeetCode 269 解題筆記,依照原始 learning note 重新整理
LeetCode 1203: Sort Items by Groups Respecting Dependencies
·2 分鐘
LeetCode Hard Graph Topological-Sort
LeetCode 1203 解題筆記,依照原始 learning note 重新整理
LeetCode 1059: All Paths from Source Lead to Destination
·1 分鐘
LeetCode Medium Graph Topological-Sort
LeetCode 1059 解題筆記:從終點反向修剪出度,一個點所有出邊都指向安全的點才安全;終點本身不能有出邊
LeetCode 1782: Count Pairs Of Nodes
·2 分鐘
LeetCode Hard Two-Pointers Graph Sorting Hash-Table Counting
LeetCode 1782 解題筆記:先用排序後的度數 + 對撞指針數 deg[a] + deg[b] > q 的配對,再對有共用邊的配對修正多算的部分
LeetCode 3910: Count Connected Subgraphs with Even Node Sum
·1 分鐘
LeetCode Hard Bit-Manipulation Enumeration Graph Union-Find
LeetCode 3910 解題筆記:n 很小,用位元遮罩枚舉所有子集合,先檢查和是不是偶數,再檢查誘導子圖是否連通
LeetCode 778: Swim in Rising Water
·2 分鐘
LeetCode Hard Graph Dijkstra Binary-Search
LeetCode 778 解題筆記,依照原始 learning note 重新整理