置頂

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

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

熱門文章

2026年10月17日 星期六

LeetCode 解題筆記:3551. Minimum Swaps to Sort by Digit Sum

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


LeetCode 題目連結:3551. Minimum Swaps to Sort by Digit Sum

解題想法


中等難度題。題目給一個長度為 $n ~(1 \leq n \leq 10^5)$ 的整數陣列 $nums ~(1 \leq nums[i] \leq 10^9)$,如果要將 $nums$ 的數字依照數字加總、數字大小由小到大排序,最少要交換 $nums$ 之中任意兩個數字幾次?這題的提示1,先將 $nums$ 依照數字加總、數字大小由小到大排序,再找出排序後的數字在原來的 $nums$ 之中的索引值。提示2,答案等於 $n - $ 環的數量。

對於提示1,我們可以寫一個自訂函式 digit_sum,用來計算整數的數字加總。將 $nums$ 之中的數字加總、數字、索引值組成 tuple 存入另一個陣列 $arr$,再用內建的 sort 將 $arr$ 排序。由於 sort 會自動依照 tuple 的內容及順序由小到大排序,不需要寫比較式。為了找出排序後的數字於 $nums$ 之中對應的索引值,我一開始是用 dict 記錄數字對索引值的資料,但是這樣寫在 C++ 會很慢。後來改成開一個長度 $n$ 的一維陣列 $target$,$target[i]$ 為 $arr[i]$ 在 $nums$ 之中的索引值,這樣速度快很多。

對於提示2,我們可以假設 $nums$ 之中有 $m$ 個環,每個環的長度分別為 $L_1, L_2, \dots, L_m$。如果要將長度為 $L$ 的環排序,交換任意兩個數字 $1$ 次可以將 $1$ 個數字歸位,共要交換 $L - 1$ 次。因此所有環交換的次數為 $$ ans = (L_1 - 1) + (L_2 - 1) + \dots + (L_m - 1) = (L_1 + L_2 + \dots + L_m) - m = n - m $$

Python 程式碼


Runtime: 885 ms, beats 94.69%. Memory: 45.08 MB, beats 75.22%.
class Solution:
    def minSwaps(self, nums: List[int]) -> int:
        # 自訂函式計算數字加總
        def digit_sum(x):
            dsum = 0
            while x:
                dsum += x % 10
                x //= 10
            return dsum

        # 將 {數字加總、num、索引值} 組成 tuple 存入 arr,再由小到大排序
        n = len(nums)  # 數量
        arr = [(digit_sum(nums[i]), nums[i], i) for i, num in enumerate(nums)]
        arr.sort()

        # 舊的索引值對應到新的索引值
        target = [-1] * n
        for i in range(n):
            target[arr[i][2]] = i
        
        # 找出同組的數字,如果同組有 m 個數字,要交換 m - 1 次
        cycle = 0  # 環的數量
        visited = [False] * n  # nums[i] 是否已經找過
        for i in range(n):
            if visited[i]: continue
            cycle += 1  # 新的環
            curr = i
            while not visited[curr]:
                visited[curr] = True
                curr = target[curr]
        return n - cycle


ZeroJudge 解題筆記:o627.校外教學分組 (Trip)

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


ZeroJudge 題目連結:o627.校外教學分組 (Trip)

題目 pdf 檔連結:校外教學分組 (Trip)

解題想法


這題是TOI練習賽202409潛力組第2題,這題也很難。題目是多組測資。每組測資第1列為2個整數 $n (3 \leq n \leq 16)$、$m (m \leq n (n-1) / 2)$,代表有 $n$ 個人,編號 $1$ 到 $n$,有 $m$ 組朋友關係。接下來 $m$ 列,每列有2個相異整數 $u, v$,代表 $u, v$ 是朋友,不能不在同一組。如果要將所有人分成3組,朋友不能分在同一組,求這3組中最多人數減去最少人數的最小值。如果無法分成3組,回傳 $-1$。

由於這題的 $n$ 最大為 16,可以用二進位制的整數 $bitmask$ 代表這 $n$ 個人所有的組合,如果 $mask$ 第 $i$ 位是 $1$,代表編號 $i$ 的人在這個分組之中,但是題目的編號是從 $1$ 開始,為了配合位元運算,需要將編號改成從 $0$ 開始。解題過程主要分為 3 步:
  1. 開一個長度為 $n$ 的陣列 $adj$,並用 $bitmask$ 儲存每個人的好友名單。
  2. 找出所有合法的組合及人數。所有的組合數 $total = 1 << n$,開一個長度為 $total$ 的陣列 $valid$ 記錄每個組合是否合法,另一個長度為 $total$ 的陣列 $sz$ 記錄每個組合的人數。
  3. 窮舉所有可能的組合,要求各組人數 $s1 \geq s2 \leq s3$,因為題目只要求分成 3 組,並沒有規定哪個組別的人數最多,加上這個條件可以少算很多狀況。
另外可以再加上一個已經找到理論最佳解的剪枝條件,如果已經找到最佳解,之後不可能再找到更好的解,可以直接輸出答案不需要再跑。如果 $n$ 是 $3$ 的倍數,可以分為人數相等的 $3$ 組,理論最佳解為 $0$;反之,理論最佳解為 $1$。在第 3 個步驟之前,可以將答案 $ans$ 定義成超出題目上限的值,例如 $1000$,如果在第 3 個步驟中沒有找到解,$ans$ 仍為預設值,代表無解,輸出 $-1$。

Python 程式碼


使用時間約為 0.5 s,記憶體約為 10.8 MB,通過測試。
def solve():
    import sys
    
    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

        # 1. 用 bitmask 儲存朋友關係
        adj = [0] * n
        for _ in range(m):
            u = int(data[ptr]) - 1  # u, v 是朋友,改成 0-index
            v = int(data[ptr + 1]) - 1
            ptr += 2
            adj[u] |= (1 << v)
            adj[v] |= (1 << u)

        # 2. 找出所有合法的組合及人數
        total = 1 << n  # 所有的組合數
        valid = [True] * total  # 組合是否合法
        sz = [0] * total  # 各組合的人數
        for mask in range(1, total):
            sz[mask] = mask.bit_count()  # 計算 mask 之中 1 的數量,就是人數
            u = (mask & -mask).bit_length() - 1  # 取 mask 的 lowbit 計算二進位制長度 - 1,即最後的 1 索引值
            prev = mask ^ (1 << u)  # 去除 u 之後的組合
            # 如果 prev 不合法,或者 mask 之中有人是 u 的朋友,mask 不合法
            if not valid[prev] or mask & adj[u]:
                valid[mask] = False

        # 3. 窮舉所有可能的組合,要求各組人數 s1 >= s2 >= s3
        ans = 1000  # 答案預設為超過上限的值
        full = total - 1  # n 個人同一組的 mask
        best = 0 if n%3 == 0 else 1  # 理論上的最佳解,如果 n 是 3 的倍數,可以平分成 3 組,最佳解為 0;否則最佳解為 1
        found = False  # 是否找到理論最佳解
        for s1 in range(1, total):
            # 如果 s1 不合法,找下一個
            if not valid[s1]: continue
            # 剪枝1,如果 s1 人數小於 n//3,找下一個
            if sz[s1] * 3 < n: continue
            # 用 XOR 找出剩下的人
            rest = full ^ s1
            # 窮舉 s2,逐步縮小 s2
            s2 = rest
            while s2 > 0:
                # 如果 s2 合法
                if valid[s2]:
                    # 用 XOR 找出第 3 組 s3
                    s3 = rest ^ s2
                    # 如果 s3 合法
                    if valid[s3]:
                        # 剪枝2,要求人數 s1 >= s2 >= s3
                        if sz[s1] >= sz[s2] >= sz[s3]:
                            ans = min(ans, sz[s1] - sz[s3])
                            # 剪枝3,找到理論最佳解,提早終止
                            if ans == best:
                                result.append(f"{best:d}\n")
                                found = True
                                break
                # 縮小 s2,不加這行會變成無窮迴圈
                s2 = (s2 - 1) & rest
            # 已經找到理論最佳解,中止迴圈
            if found: break
        # 如果沒有找到理論最佳解,ans == 1000,無解
        if not found:
            if ans == 1000: result.append("-1\n")
            else: result.append(f"{ans:d}\n")
    # 一次性輸出答案
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年10月16日 星期五

LeetCode 解題筆記:695. Max Area of Island

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


LeetCode 題目連結:695. Max Area of Island

解題想法


中等難度題。題目給一個 $m \times n$ 的二維陣列 $grid$,其中只有代表陸地的 $1$ 以及代表水域的 $0$,並且假設 $grid$ 四周都是 $0$。陸地可以往右、下、左、上等四個方向連接。要找出 $grid$ 之中面積最大的島嶼,也就是由最多的 $1$ 組成的連通區塊。連通區塊的題目,用 BFS 或 DFS 都可以解,由於我是以 Python 為主要的解題語言,我比較習慣用 BFS,如果用 DFS 經常會遇到遞迴深度過深的問題。這題我兩個方法都有寫,如果用 Python 解題 BFS 跑比較快,如果用 C++ 解題 DFS 跑比較快。

Python 程式碼


BFS. Runtime: 11 ms, beats 96.45%. Memory: 19.38 MB, beats 94.88%.
from collections import deque

class Solution:
    def maxAreaOfIsland(self, grid: list[list[int]]) -> int:
        m, n, imax = len(grid), len(grid[0]), 0
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 0:
                    continue
                grid[i][j] = 0
                area = 1
                que = deque([(i, j)])
                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] == 1:
                            grid[nr][nc] = 0
                            area += 1
                            que.append((nr, nc))
                imax = max(imax, area)
        return imax


DFS. Runtime: 17 ms, beats 67.40%. Memory: 20.39 MB, beats 64.21%.
class Solution:
    def maxAreaOfIsland(self, grid: list[list[int]]) -> int:
        m, n, imax = len(grid), len(grid[0]), 0
        area = 0
        
        # 自訂 dfs 函式,找連通區塊面積同時將走過的格子標記為 0
        def dfs(r, c):
            nonlocal area
            area += 1
            grid[r][c] = 0
            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] == 1:
                    dfs(nr, nc)
        
        # 掃過每一格,跳過 0,遇到 1 呼叫 dfs
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 0:
                    continue
                # area 歸零之後再呼叫 dfs 找連通區塊面積
                area = 0
                dfs(i, j)
                imax = max(imax, area)
        return imax


ZeroJudge 解題筆記:o626.檔案順序 (Files)

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


ZeroJudge 題目連結:o626.檔案順序 (Files)

題目 pdf 檔連結:檔案順序 (Files)

解題想法


這題是TOI練習賽202409潛力組第1題,這題跟同組題目的第2題〈o627.校外教學分組 (Trip)〉都很難,第3題〈o628.天秤 (Libra)〉比較簡單。題目是多組測資。每組測資第1列為2個整數 $n (1 \leq n \leq 2 \times 10^5)$、$k (1 \leq k \leq 2 \times 10^5)$,分別代表有 $n$ 個檔案、$k$ 次操作。接下來 $n$ 列,每列有 4 個整數,分別代表檔案的名稱、修改時間、類型、大小。最後一列有 $k$ 個 1 到 4 的數字,分別代表用檔案的名稱、修改時間、類型、大小作為排序依據。如果有多個檔案這個屬性的值相同,則這幾個檔案保持前一次排序後的順序,也就是用 stable sort。

如果前一次操作與這次操作用的屬性不同,則這次操作為升序排序 (ascending order),也就是由小到大排序;如果重複點同樣的屬性第二次,會改成降序排序 (descending order),也就是由大到小排序。這點在解題時特別重要,因為 $n, k$ 的數值極大,如果真的模擬每一次的排序過程絕對會超時。但由於這個操作上的特性,我們可以用一個串列 $ops$ 記錄操作的索引值 $idx$ 以及目前為升序 (1) 或降序 (-1) 排列,如果連續使用相同的屬性排序,只要更新升序或降序即可;如果用不同屬性排序,於 $ops$ 最後面接上 $[idx, 1]$。

另一個要利用的特性是操作過程中如果先用了某個屬性排序,再用另一個屬性排序,之後再回來用前面用過的屬性排序,只有最後面這次的排序有效。例如先用名稱,再用修改時間,之後再回來用名稱排序,則第一次排序的效果會被後面同樣用名稱排序的效果蓋掉。因此,我們可以倒序讀取 $ops$,取出用這 4 個屬性排序的索引值及升序、降序,資料存入串列 sort_key。將 sort_key 組成 tuple,只排序一次,將排序後的檔案依序輸出。

不過這題的記憶體限制極小,只有 64 MB,我用 Python 解題最多只能通過 50% 的測資就遇到 MemoryError,目前看排行還沒有人用 Python 過關。用 C++ 則是 0.2 s、9.4 MB 過關。

Python 程式碼


只能通過 40% 的測資。
def solve():
    import sys

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        k = int(data[ptr + 1])
        ptr += 2
        files = []  # (name, time, type, size)
        for _ in range(n):
            file = tuple(map(int, data[ptr : ptr + 4]))
            ptr += 4
            files.append(file)
        
        operations = []  # idx of operation
        state = [0] * 5  # 1 ascending, -1 descending, 0 none
        for _ in range(k):
            idx = int(data[ptr])
            ptr += 1
            operations.append(idx)
            if state[idx] == 0:
                state[idx] = 1
            else:
                state[idx] *= -1
        
        sort_key = []  # (idx, 1 or -1)
        used = [False] * 5
        for idx in operations[::-1]:
            if not used[idx]:
                used[idx] = True
                sort_key.append((idx, state[idx]))
        
        def mykey(file):
            return tuple(file[idx - 1] * val for idx, val in sort_key)
        
        files.sort(key = mykey)
        
        for file in files:
            res = " ".join(map(str, file)) + "\n"
            result.append(res)
        
    # 輸出答案
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年10月15日 星期四

ZeroJudge 解題筆記:h192.黏包、分包

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


ZeroJudge 題目連結:h192.黏包、分包

解題想法


基礎題庫裡面新出現的字串處理題。每組測資是多行輸入,要先將所有的測資接成一個很長的字串 $s$。假設 $s$ 長度為 $n$,用一個 while 迴圈從 $i = 0$ 開始依序讀取字元,分成以下 2 種狀況:
  1. 如果 $s[i]$ 不是 :,代表目前讀到的是代表長度的數字,將 $s[i]$ 暫存到字串 $u$。
  2. 如果 $s[i]$ 是 :,將 $u$ 轉成整數 $d$,結算長度為 $d$ 的子字串,可以用 Python 的字串切片或是 C++ 的 substr,從 $s[i+1]$ 開始取子字串,組合成題目規定的輸出格式,輸出完畢後重置字串 $u$ 並將 $i += d$。
每次執行完 while 迴圈記得要 $i += 1$,否則會變成無窮迴圈。

Python 程式碼


解題時間約為 12 ms,使用記憶體約為 9.6 B。
def solve():
    import sys
    
    result = []
    # 讀取多行測資,全部接成一行
    s = "".join(sys.stdin.read().split())
    
    # 依序讀取字元暫存至字串 u,讀到 : 輸出長度 u 的子字串
    i, n = 0, len(s)
    u = ""
    while i < n:
        if s[i] == ':':
            d = int(u)
            result.append(f"{u}:{s[i+1 : i+d+1]}\n")
            i += d
            u = ""
        else:
            u += s[i]
        i += 1
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


LeetCode 解題筆記:2913. Subarrays Distinct Element Sum of Squares I

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


LeetCode 題目連結:2913. Subarrays Distinct Element Sum of Squares I

解題想法


簡單題。題目給一個長度 $n = 1$ 到 $100$ 的陣列 $nums$,且 $1 \leq nums[i] \leq 100$,要計算一個連續區間的子陣列之中獨特數字的數量,將數量平方加到答案 $ans$ 之中。回傳所有子陣列的獨特數字數量平方加總。

由於陣列長度不大,直接用兩層 for 迴圈跑過所有的子陣列速度很快。可以用集合記錄子陣列中的獨特數字,也可以用一個長度為 $101$ 的陣列記錄範圍內 $0$ 到 $100$ 各個數字的數量。由於 Python 的集合運算速度很快,用集合寫很方便。但如果用 C++ 解題,即使用 unordered_set 計數速度也有點慢,用陣列計數會比較快。

Python 程式碼


Runtime: 19 ms, beats 82.77%. Memory: 19.32 MB, beats 27.38%.
class Solution:
    def sumCounts(self, nums: List[int]) -> int:
        n, ans = len(nums), 0 # 長度,答案
        # 以 i 為終點,找 j = i-1 ~ 0 範圍內獨特數字的數量並更新答案
        for i in range(n):
            distinct = {nums[i]}  # 用集合存數字
            ans += 1  # 答案加 1,集合內只有 nums[i]
            for j in range(i-1, -1, -1):
                distinct.add(nums[j])  # 加入 nums[j]
                ans += len(distinct)**2
        return ans


Runtime: 21 ms, beats 62.15%. Memory: 19.07 MB, beats 98.15%.
class Solution:
    def sumCounts(self, nums: List[int]) -> int:
        n, ans = len(nums), 0 # 長度,答案
        # 以 i 為終點,找 j = i-1 ~ 0 範圍內獨特數字的數量並更新答案
        for i in range(n):
            distinct = [0] * 101  # 範圍內 1 ~ 100 各數字的數量
            distinct[nums[i]] = 1  # nums[i] 數量為 1
            curr = 1  # 範圍內獨特數字的數量
            ans += 1  # 答案加 1,集合內只有 nums[i]
            for j in range(i-1, -1, -1):
                distinct[nums[j]] += 1  # nums[j] 數量加 1
                if distinct[nums[j]] == 1: curr += 1
                ans += curr * curr
        return ans


2026年10月14日 星期三

LeetCode 解題筆記:3723. Maximize Sum of Squares of Digits

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


LeetCode 題目連結:3723. Maximize Sum of Squares of Digits

解題想法


中等難度題。題目給兩個整數 $num, sum$,代表共有 $num$ 個數字,所有數字加總等於 $sum$。對於符合這兩個條件的數字,其分數為每個位數平方的加總,找出分數最高的數字並以字串格式回傳,如果無解則回傳空字串。這題下方的提示基本上已經講清楚解法,原則上盡量選用數字 9,最後再加上剩下的數字或 0,如果最後找到的答案位數大於 $num$ 無解。

Python 程式碼


用 for 迴圈依序填入數字,速度很慢。Runtime: 2761 ms, beats 5.51%. Memory: 21.24 MB, beats 59.06%.
class Solution:
    def maxSumOfSquares(self, num: int, sum: int) -> str:
        ans = ""  # 答案,字串格式
        rem = sum  # 位數加總
        # 每個位數都由 9 開始往下找,共執行 num 次
        for _ in range(num):
            for d in range(9, -1, -1):
                if rem >= d:
                    ans += str(d)
                    rem -= d
                    break
        return ans if rem == 0 else ""


計算需要填入的數字 9 數量以及餘數,速度快很多。Runtime: 4 ms, beats 74.02%. Memory: 20.87 MB, beats 90.55%.
class Solution:
    def maxSumOfSquares(self, num: int, sum: int) -> str:
        # 答案,字串格式,左側盡量用 9,如果 sum % 9 > 0,右側補上餘數
        ans = "9" * (sum // 9) + (str(sum % 9) if sum % 9 > 0 else "")
        # 長度不夠,補 0
        if len(ans) < num: ans += "0" * (num - len(ans))
        # 長度超過 num,無解
        return ans if len(ans) == num else ""


ZeroJudge 解題筆記:p929.像素圖 (Pixel)

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


ZeroJudge 題目連結:p929.像素圖 (Pixel)

題目 pdf 檔連結:像素圖 (Pixel)

解題想法


題目是多筆測資。每筆測資第一列是 4 個整數 $n, m, p, q$,分別代表圖的尺寸為 $n \times m$,列擴長為 $p$ 倍,欄擴張為 $q$ 倍。接下來 $n$ 列每列有 $m$ 個 0 或 1,輸出擴張後的圖。先用兩層 for 迴圈依序讀取每一個數字,將擴張後長度變為 $m \times q$ 的一列內容先填好,再將擴張後的列重複印出 $p$ 次,這樣寫起來比較簡單。

Python 程式碼


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

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n, m, p, q = map(int, data[ptr : ptr + 4])
        ptr += 4
        for i in range(n):
            row = [0] * (m*q)
            for j in range(m):
                v = int(data[ptr])
                ptr += 1
                #for k in range(q):
                #    row[j*q + k] = v
                row[j*q : (j+1)*q] = [v]*q  # 用串列切片填入資料
            res = " ".join(map(str, row)) + "\n"
            for _ in range(p):
                result.append(res)
    
    # 輸出答案
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年10月13日 星期二

ZeroJudge 解題筆記:p928.雷射探測 (Laser)

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


ZeroJudge 題目連結:p928.雷射探測 (Laser)

題目 pdf 檔連結:雷射探測 (Laser)

解題想法


題目是多筆測資。每筆測資第一列是兩個整數 $n, m$,代表地圖的尺寸為 $n \times m$。接下來共有 $n$ 列,每列皆為長度 $m$ 的字串,其中 . 代表空格,R 代表右斜鏡,L 代表左斜鏡,# 代表牆壁。雷射從地圖左上角 (0, 0) 的位置出發,方向向右。題目中有說明雷射光碰到右斜鏡、左斜鏡時方向變化的方式。要計算雷射光經過的格子數量,如果同一格經過不只一次,也只算一格。

這題需要判斷雷射光前進到下一格時,是否碰到反射鏡、牆壁或是出界,但是我不想要檢查邊界值,我在地圖的周圍加上一圈 #,只要碰到這個是 # 就停下來。我定義方向右、下、左、上分別對應 0, 1, 2, 3,用兩個陣列 slash、backslash 記錄右斜鏡、左斜鏡對應的方向變化方式。並用一個二維陣列 visited,記錄每個格子的走訪狀態。

Python 程式碼


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

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        # 1. 讀取測資
        n = int(data[ptr])  # 地圖 n*m
        m = int(data[ptr + 1])
        ptr += 2
        grid = [["#"] * (m + 2) for _ in range(n + 2)]  # 地圖周圍加一圈 #
        for i in range(1, n + 1):  # 讀取地圖
            row = ["#"] + list(data[ptr]) + ["#"]
            grid[i] = row
            ptr += 1
        
        # 2. 從左上角出發找答案
        slash = (3, 2, 1, 0)  # 右斜鏡方向變化方式,右0、下1、左2、上3
        backslash = (1, 0, 3, 2)  # 左斜鏡方向變化方式,右0、下1、左2、上3
        dr, dc = (0, 1, 0, -1), (1, 0, -1, 0)  # 右、下、左、上位移量
        curr = 0  # 目前的方向,初始值向右
        ans = 0  # 經過的格子數
        r, c = 1, 1  # 目前的位置,加了一圈 #,起點改成 (1, 1)
        visited = [[False] * (m + 2) for _ in range(n + 2)]  # 走訪狀態
        while True:
            if not visited[r][c]:  # 還沒有走過的格子,答案加 1
                ans += 1
            visited[r][c] = True
            r += dr[curr]
            c += dc[curr]
            if grid[r][c] == '#':  # 遇到牆,停止
                break
            elif grid[r][c] == 'R':  # 右斜鏡
                curr = slash[curr]
            elif grid[r][c] == 'L':  # 左斜鏡
                curr = backslash[curr]

        result.append(f"{ans}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


LeetCode 解題筆記:3727. Maximum Alternating Sum of Squares

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


LeetCode 題目連結:3727. Maximum Alternating Sum of Squares

解題想法


中等難度題,但其實寫起來很簡單。題目給一個長度為 $n$ 的整數陣列 $nums$,經過適當的排列之後,要找出 $score = nums[0]^2 - nums[1]^2 + nums[2]^2 - nums[3]^3 + \dots$ 的最大值。我的寫法是先將 $nums$ 所有的項目都改成平方,由小到大排序,答案 $ans$ 加上 $nums[n//2]$ 到 $nums[n-1]$,減去索引值 $nums[0]$ 到 $nums[n//2 - 1]$。不需要考慮 $n$ 的奇偶性,因為計算整數除法時,如果 $n$ 是奇數 $n//2$ 會自動捨去小數,將 $nums[n//2]$ 留給後半段要加到 $ans$ 的部分。

Python 程式碼


Runtime: 178 ms, beats 61.54%. Memory: 32.61 MB, beats 73.63%.
class Solution:
    def maxAlternatingSum(self, nums: List[int]) -> int:
        # 先將每一項都平方,由小到大排序,減去 nums[:n//2],加上 nums[n//2:]
        nums = sorted(num**2 for num in nums)
        n = len(nums)  # 長度
        return sum(nums[n//2:]) - sum(nums[:n//2])


C++ 程式碼


Runtime: 66 ms, beats 37.15%. Memory: 176.10 MB, beats 70.59%.
class Solution {
public:
    long long maxAlternatingSum(vector<int>& nums) {
        // 先將每一項都平方,由小到大排序,減去 nums[:n//2],加上 nums[n//2:]
        int n = (int)nums.size();  // 長度
        for(int i = 0; i < n; i++) {
            nums[i] *= nums[i];
        }
        sort(nums.begin(), nums.end());
        return accumulate(nums.begin() + n/2, nums.end(), 0LL) - accumulate(nums.begin(), nums.begin() + n/2, 0LL);
    }
};