置頂

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

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

熱門文章

2026年10月28日 星期三

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


set. Runtime: 725 ms, beats 90.83%. Memory: 25.57 MB, beats 93.33%.
class Solution:
    def minCost(self, grid: list[list[int]]) -> int:
        m, n = len(grid), len(grid[0])
        # dp[j] 代表走到 grid[i][j] 可能的路徑 XOR 值
        dp = [set() for _ in range(n)]
        
        for i in range(m):
            for j in range(n):
                if i == 0 and j == 0:  # 起點
                    dp[0].add(grid[0][0])
                else:  # 其它位置
                    pre = set()  # 前一個
                    if i > 0:  pre.update(dp[j])  # 從上往下走
                    if j > 0:  pre.update(dp[j-1])  # 從左往右走
                    
                    # 找可能的路徑 XOR 值
                    val = grid[i][j]
                    dp[j] = {x ^ val for x in pre}  # 新的狀態
                  
        # 在終點 n-1 找最小值
        return min(dp[n-1])


C++ 程式碼


Runtime: 74 ms, beats 98.76%. Memory: 40.43 MB, beats 92.98%.
class Solution {
public:
    int minCost(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();  // m 列、n 欄
        // dp[j] 代表走到 grid[i][j] 可能的路徑 XOR 值,範圍為 0 ~ 1023
        // 如果 dp[j][k] = true,代表有值為 k 的路徑
        vector<bitset<1024>> dp (n);
        // 測試 i = 0 ~ m-1 列,試著走到 dp[j]
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                bitset<1024> nxt;  // 新的狀態
                if (i == 0 && j == 0) {  // 起點,grid[0][0]
                    nxt.set(grid[0][0]);
                } else {  // 一般狀況
                    bitset<1024> pre;  // 前一個狀態
                    if (i > 0) pre |= dp[j];  // 從上往下走
                    if (j > 0) pre |= dp[j-1];  // 從左往右走
                    int val = grid[i][j];
                    for(int k = 0; k < 1024; k++) {  // 更新可能的值
                        if (pre.test(k)) {  // 如果 pre[k] 為 true
                            nxt.set(k ^ val);  // nxt[k ^ val] 設定為 true
                        }
                    }
                }
                dp[j] = nxt;  // 更新 dp[j]
            }
        }
        // 於 dp[n-1] 之中,由小到大找第一個為 true 的 k 值
        for(int k = 0; k < 1024; k++) {
            if (dp[n-1].test(k)) {
                return k;
            }
        }
        return 0;  // 預設的回傳值
    }
};


沒有留言:

張貼留言