熱門文章

2026年4月30日 星期四

ZeroJudge 解題筆記:d192. 11351 - Last Man Standing

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


ZeroJudge 題目連結:d192. 11351 - Last Man Standing

解題想法


輸入的測資範圍應該是 $0 < n \leq 10^5$ 及 $0 < k < 10^9$,如果模擬整個淘汰的過程會超時。 從最後的贏家逆推𠩤來的編號。
  1. 為了方便運算,成員的編號由0開始,最後輸出結果時再加1。
  2. 先假設幸運者的編號 $idx_{0} = 0$,最後剩下 1 個人。
  3. 第 $n-1$ 次(最後1次)運作時,參與的人數為 $2$,幸運者原來的編號為 $idx_{n-2} = k \% 2$。
  4. 第 $n-2$ 次(倒數第2次)運作時,參與的人數為 $3$,幸運者原來的編號為 $idx_{n-3} = (idx_{n-2} + k) \% 3$。
  5. 第 $n-3$ 次(倒數第3次)運作時,參與的人數為 $4$,幸運者原來的編號為 $idx_{n-4} = (idx_{n-3} + k) \% 4$。
  6. 若 $i = 1、2、\dots、n$,則第 $n-i$ 次(倒數第 $i$ 次)運作時,參與的人數為 $1 + i$,幸運者原來的編號為 $idx_{i} = (idx_{i-1} + k) \% (1+ i)$。
  7. 輸出幸運者的編號,由於題目的編號從 $1$ 開始,需要將 $idx$ 加 $1$。


Python 程式碼


模擬過程,超時。
t = int(input())
for i in range(1, t+1):
    n, k = map(int, input().split())
    arr = list(range(1, n+1))  # 目前還存活的人
    idx = 0  # 從 1 號、索引值 0 開始
    while n > 1:  # 人數大於 1 繼續執行
        idx = (idx+k-1) % n  # 更新淘汰者的索引值
        del arr[idx]  # 移除 arr[idx]
        n -= 1  # 人數減 1
        idx %= n  # 更新 idx,有可能歸零
    print(f"Case {i:d}: {arr[0]:d}")  # 印出答案

使用時間約為 58 ms,記憶體約為 8.2 MB,通過測試。
t = int(input())
for i in range(1, t+1):
    n, k = map(int, input().split())
    idx = 0  # 最後剩下的人,索引值 0
    for j in range(1, n):  # 1 到 n-1
        idx = (idx + k) % (1+j)
    print(f"Case {i:d}: {idx+1:d}")  # 印出答案


2026年4月29日 星期三

ZeroJudge 解題筆記:d191. 11352 - Crazy King

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


ZeroJudge 題目連結:d191. 11352 - Crazy King

解題想法


因為我不想要在檢查騎士可以走到的位置時,需要先檢查這一步是不會出界,我會在地圖的周圍加上兩層 $-1$ 當作哨兵,用 $-1$ 標示邊界及不能走到的格子,假設地圖尺寸原為 $m \times n$,如果用 Python 解題會改成尺寸為 $(m+2) \times (n+2)$ 的二維串列,如果用 C++ 解題會改成尺寸為 $(m+4) \times (n+4)$ 的二維陣列。地圖 $grid$ 之中實際上儲存的是步數,用 $0$ 代表起點,$-2$ 代表終點,$-3$ 代表空格及未走過的格子,$-1$ 代表邊界及不能走的格子。讀取地圖資料,依照字元標示 $grid$ 的數字,再檢查騎士的位置,將騎士可以一步走到的格子也標示成 $-1$。最後再用 BFS,找出國王從 $A$ 點走到 $B$ 點的最少步數。BFS 結束之後,如果終點對應的格子仍為 $-2$,代表無法走到終點。

Python 程式碼


使用時間約為 24 ms,記憶體約為 9.2 MB,通過測試。
from collections import deque

t = int(input())
for _ in range(t):
    """ 讀取地圖資料 """
    m, n = map(int, input().split())  # 棋盤 m*n,實際上儲存的是步數
    grid = [[-1]*(n+2) for _ in range(m+2)]  # 最右側、最下方加上兩層 -1 當作哨兵
    knight = []  # 騎士的位置
    sr, sc, er, ec = 0, 0, 0, 0  # 起點、終點
    for r in range(m):  # 讀取 m 行資料
        line = input()
        for c, ch in enumerate(line):  # 依序讀取 line 的字元
            if ch == 'Z': knight.append((r, c))  # 騎士
            elif ch == 'A':  # 起點
                grid[r][c] = 0; sr = r; sc = c;
            elif ch == 'B':  # 終點
                grid[r][c] = -2; er = r; ec = c;
            elif ch == '.':  # 空格
                grid[r][c] = -3
    for r, c in knight:  # 依序讀取騎士所在的位置
        # 依序檢查右上、右、右下、下、左下、左、左上、上
        for dr, dc in ((-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1)):
            nr, nc = r+dr, c+dc  # 檢查 (nr, nc)
            if grid[nr][nc] == -3:  # 空格
                grid[nr][nc] = -1  # 設定成 -1,不能走
    """ BFS 找最短路徑 """
    que = deque([(sr, sc)])  # 待走訪佇列
    end = False
    while que and not end:  # 如果 que 還有資料而且還沒有走到終點,繼續執行
        r, c = que.popleft()  # 取出 que 最前面的資料
        # 依序檢查右上、右、右下、下、左下、左、左上、上
        for dr, dc in ((-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0)):
            nr, nc = r+dr, c+dc  # 檢查 (nr, nc)
            if grid[nr][nc] == -3:  # 走到空格
                grid[nr][nc] = grid[r][c] + 1  # 更新步數
                que.append((nr, nc))  # (nr, nc) 加到 que
            elif grid[nr][nc] == -2:  # 走到終點
                grid[nr][nc] = grid[r][c] + 1  # 更新步數
                end = True; break  # 走到終點,中止迴圈
    """ 印出答案 """
    if grid[er][ec] == -2:  # 無法走到終點
        print("King Peter, you can't go now!")
    else:  # 走到終點
        print(f"Minimal possible length of a trip is {grid[er][ec]:d}")


2026年4月28日 星期二

ZeroJudge 解題筆記:d190. 11462 - Age Sort

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


ZeroJudge 題目連結:d190. 11462 - Age Sort

解題想法


這題就是單純的排序,只是改成多組測資,以 0 結尾。如果用 Python 解題,可以先將 $n$ 個數字存入串列 arr,再用 arr.sort() 由小到大排序,最後用 print(*arr) 輸出;也可以用 sorted 將 map 轉換後的結果排序並用 print 輸出。如果想要測試一些特別的寫法,也可以用最小優先佇列解題,依序彈出佇列中的最小值,但其實這樣寫比較慢,對本題而言沒有必要。

Python 程式碼


使用時間約為 0.5 s,記憶體約為 157.1 MB,通過測試。
while True:
    n = int(input())
    if n == 0: break
    arr = list(map(int, input().split()))
    arr.sort()
    print(*arr)

使用時間約為 0.5 s,記憶體約為 157.3 MB,通過測試。
while True:
    n = int(input())
    if n == 0: break
    print(*sorted(map(int, input().split())))

用 sys.stdin 及 sys.stdout.write 加速,使用時間約為 0.4 s,記憶體約為 304.3 MB,通過測試。
def solve():
    import sys
    
    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        if n == 0: break
        arr = sorted(map(int, data[ptr:ptr+n]))
        ptr += n
        res = " ".join(map(str, arr))
        result.append(f"{res}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()

heapq,反而更慢。使用時間約為 1.1 s,記憶體約為 157.4 MB,通過測試。
import heapq

while True:
    n = int(input())
    if n == 0: break
    pq = [i for i in map(int, input().split())]
    heapq.heapify(pq)
    while len(pq) > 1: print(heapq.heappop(pq), end=" ")
    print(heapq.heappop(pq))


2026年4月27日 星期一

ZeroJudge 解題筆記:d188. 11342 - Three-square

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


ZeroJudge 題目連結:d188. 11342 - Three-square

解題想法


用字典建表,找出 $0$ 到 $50000$ 之間,由 $3$ 個非負完全平方數字相加的字典順序最小組合,最大只要測試到 $i = \sqrt{50000}$ 就好。用 3 層 for 迴圈建表,最外層跑 $i = 0$ 到 $i = \sqrt{50000}$,第 2 層跑 $j = i$ 到 $j = \sqrt{50000}$,第 3 層跑 $k = j$ 到 $k = \sqrt{50000}$,如果加總只要記錄第一次跑出來的 $i, j, k$ 就好。接下來讀取測資,查表,輸出答案。

Python 程式碼


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

    """ 建表,找出 0 ~ 50000 字典順序最小的組合 """
    max_k = 50000  # 題目的測資最大值
    max_i = int(max_k**0.5) + 1  # 可能的數字
    table = dict()  # 表格
    for i in range(max_i):  # 測試 i = 0 ~ max_i-1
        x = i*i  # 儲存 i 平方的值
        for j in range(i, max_i):  # 測試 j = 0 ~ max_i-1
            y = x + j*j  # 加上 j 平方,不能寫成 x += j*j,變數值會出問題
            if y > max_k: break  # 超過 max_k,中止迴圈
            for k in range(j, max_i):  # 測試 j = 0 ~ max_i-1
                z = y + k*k  # 加上 k 平方,不能寫成 y += k*k,變數值會出問題
                if z > max_k: break  # 超過 max_k,中止迴圈
                if z not in table:  # 如果 z 不在 table 之中
                    table[z] = (i, j, k)  # 設定為 (i, j, k)

    """ 讀取測資,輸出答案 """
    result = []
    data = sys.stdin.read().split()
    ptr = 1
    while ptr < len(data):
        n = int(data[ptr])  # 要測試的數值 n
        ptr += 1
        if n not in table:  # 如果 n 不在 table 之中,印出 -1
            result.append("-1\n")
        else: # 反之,印出 table[n] 的值
            res = " ".join(map(str, table[n])) + "\n"
            result.append(res)
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月26日 星期日

ZeroJudge 解題筆記:d187. 11530 - SMS Typing

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


ZeroJudge 題目連結:d187. 11530 - SMS Typing

解題想法


這題可以用字典建表,先記錄每個字元對應的次數,讀取測資後再依序取出字元,計算次數加總。

Python 程式碼


使用時間約為 7 ms,記憶體約為 8.2 MB,通過測試。
table = {'a': 1, 'b': 2, 'c': 3,
         'd': 1, 'e': 2, 'f': 3,
         'g': 1, 'h': 2, 'i': 3,
         'j': 1, 'k': 2, 'l': 3,
         'm': 1, 'n': 2, 'o': 3,
         'p': 1, 'q': 2, 'r': 3, 's': 4,
         't': 1, 'u': 2, 'v': 3,
         'w': 1, 'x': 2, 'y': 3, 'z': 4,
         ' ': 1}

n = int(input())
for i in range(1, n+1):
    s = input()
    cnt = sum(table[c] for c in s)
    print(f"Case #{i:d}: {cnt:d}")


2026年4月25日 星期六

ZeroJudge 解題筆記:d186. 11461 - Square Numbers

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


ZeroJudge 題目連結:d186. 11461 - Square Numbers

解題想法


先將 $a$ 開根號並向上取整,計算範圍內平方數開根根號的最小值 $low$,再對 $b$ 開根號並向下取整,計算範圍內平方數開根根號的最大值 $high$,答案就是 $high - low + 1$。

Python 程式碼


使用時間約為 7 ms,記憶體約為 8.6 MB,通過測試。
def solve():
    import sys, math
    
    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        a, b = map(int, data[ptr:ptr+2])
        ptr += 2
        if a == 0 and b == 0: break
        low = math.ceil(math.sqrt(a))
        high = math.floor(math.sqrt(b))
        result.append(f"{high - low + 1:d}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月24日 星期五

ZeroJudge 解題筆記:d183. 00725 - Division

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


ZeroJudge 題目連結:d183. 00725 - Division

解題想法


題目要求將 $0$ 到 $9$ 分配成兩個 $5$ 位數 $a, b$,而且開頭可以為 $0$,代表可能的數字下限為 $1234$、上限為 $98765$,而且 $a$ 可以整除 $b$,商為 $2$ 到 $79$,所以 $a$ 的範圍大約是 $1234$ 到 $49876$, $b$ 的範圍大約是 $49876$ 到 $98765$。另外再寫兩個自訂函式,檢查兩個數是否有重覆的數字。建表,計算商為 $2$ 到 $79$ 可能的除數、被除數組合,再查表找答案。 用 Python 的 itertools.permutations 也可以窮舉所有數字的組合,但這樣會多跑很多不可能的數字,速度會比較慢。

Python 程式碼


使用時間約為 80 ms,記憶體約為 8.8 MB,通過測試。
import sys

def check_self(s):
    """ 代入字串,檢查是否有重複的數字 """
    used = set()  # 已經出現的數字
    for c in s:  # 依序讀取字元 c
        if c in used:  # 如果 c 已經出現過
            return False  # 回傳 False
        used.add(c)  # c 加入 used
    return True  # 如果可以跑到這行,回傳 True

def check_other(s, t):
    """ 代入字串 s, t,檢查是否有重複的數字 """
    used = set(list(s))  # s 已經檢查過,直接轉成 set
    for c in t:  # 依序讀取字元 c
        if c in used:  # 如果 c 已經出現過
            return False  # 回傳 False
        used.add(c)  # c 加入 used
    return True  # 如果可以跑到這行,回傳 True

""" 建表 """
low, mid, high = 1234, 49876, 98765  # 除數下限、上限,被除數上限
table = [[] for _ in range(80)]  # 表格,各種商的組合
for a in range(low, mid+1):  # 依序測試可能的除數 a
    n = str(a).zfill(5)  # 轉成字串,前面補 0 至 5 位數
    if not check_self(n): continue  # 如果 n 有重複的數字,找下一個
    for i in range(2, 80):  # 依序測試可能的倍數 i
        b = a*i  # 被除數 b
        if b >= high: break  # 如果 b 超過上限,不可能再有解,中止迴圈
        m = str(b).zfill(5)  # 轉成字串,前面補 0 至 5 位數
        if check_self(m) and check_other(n, m):  # 如果 m 本身、m 與 n 沒有重複的數字
            table[i].append((m, n))  # (m, n) 加到 table[i]

""" 讀取測資,組合答案 """
result = []
for line in sys.stdin:
    N = int(line)
    if N == 0: break
    if result: result.append("\n")  # 如果 result 已經有內容,先換行
    if not table[N]:  # 如果 table[N] 是空的,無解
        result.append(f"There are no solutions for {N:d}.\n")
    else:  # 反之,依序讀取 table[N] 的內容組成答案
        for t in table[N]:
            result.append(f"{t[0]} / {t[1]} = {N:d}\n")
sys.stdout.write("".join(result))

2026年4月23日 星期四

ZeroJudge 解題筆記:d182. 00256 - Quirksome Squares

作者:王一哲
日期:2026年4月23日


ZeroJudge 題目連結:d182. 00256 - Quirksome Squares

解題想法


假設位數為 $n$,題目要找的答案只有可能是 $i = 0, 1, 2, \dots, 98, 10^{n/2} - 1$ 的平方,將 $i^2$ 的左、右兩半加起來,再檢查組合後的數平方是否等於 $i^2$,就可以找到答案。

Python 程式碼


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

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        m = 10**(n//2)  # 上限,10 的 n/2 次方
        for i in range(m):
            square = i*i  # 平方
            num = square//m + square%m  # 左、右兩半相加
            if num**2 == square:  # 符合條件
                ans = str(square).zfill(n)  # 前方補 0 至 n 位數
                result.append(f"{ans}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月22日 星期三

ZeroJudge 解題筆記:d174. 10297 - Beavergnaw

作者:王一哲
日期:2026年4月22日


ZeroJudge 題目連結:d174. 10297 - Beavergnaw

解題想法


先看最右上角剩下部分的截面,相當於是一個底、高皆為 $\frac{D-d}{2}$ 的等腰直角三角形,因此上、下兩塊截面為梯形的部分,是兩個圓錐相減,兩者的底分別為 $\frac{D}{2}$ 及 $\frac{d}{2}$,圓錐相減後的體積為 $$ \frac{1}{3} \pi \cdot \left( \frac{D}{2} \right)^2 \cdot \frac{D}{2} - \frac{1}{3} \pi \cdot \left( \frac{d}{2} \right)^2 \cdot \frac{d}{2} = \frac{\pi}{24} (D^3 - d^3) $$ 中間直徑為 $d$ 的圓柱體積為 $$ \pi \cdot \left( \frac{d}{2} \right)^2 \cdot d = \frac{\pi}{4} d^3 $$ 直徑為 $D$ 的圓柱體積為 $$ \pi \cdot \left( \frac{D}{2} \right)^2 \cdot D = \frac{\pi}{4} D^3 $$ 切掉的體積為 $$ V = \frac{\pi}{4} D^3 - \frac{\pi}{4} d^3 - 2 \times \frac{\pi}{24} (D^3 - d^3) = \frac{\pi}{6} (D^3 - d^3) ~\Rightarrow~ d = \left( D^3 - \frac{6V}{\pi} \right)^{\frac{1}{3}} $$

Python 程式碼


使用時間約為 52 ms,記憶體約為 22 MB,通過測試。
def solve():
    import sys, math
    
    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        D = int(data[ptr])
        V = int(data[ptr+1])
        ptr += 2
        if D == 0 and V == 0: break
        d = (D**3 - 6*V/math.pi)**(1/3)
        result.append(f"{d:.3f}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月21日 星期二

ZeroJudge 解題筆記:d150. 11369 - Shopaholic

作者:王一哲
日期:2026年4月21日


ZeroJudge 題目連結:d150. 11369 - Shopaholic

解題想法


先將價格由大到小排序存入串列 $prices$,每次取 $3$ 項,將第 $3$ 項加入答案 $ans$。也可以用最大優先佇列,將價格全部存入最大優先佇列 $pq$,每次從 $pq$ 彈出目前的最高價格,每彈出 $3$ 個值就將第 $3$ 個值加入答案 $ans$。因為這題不需要一直將值推入 $pq$ 之中,可以用排序就好,速度比較快。

Python 程式碼


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

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        t = int(data[ptr])
        ptr += 1
        for _ in range(t):
            n = int(data[ptr])
            ptr += 1
            prices = sorted(map(int, data[ptr:ptr+n]), reverse=True)
            ptr += n
            ans = sum(p for p in prices[2::3])
            result.append(f"{ans:d}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()

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

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        t = int(data[ptr])
        ptr += 1
        for _ in range(t):
            n = int(data[ptr])
            ptr += 1
            pq = []
            for __ in range(n):
                p = int(data[ptr])
                ptr += 1
                heapq.heappush(pq, -p)
            ans, cnt = 0, 0
            while pq:
                p = -heapq.heappop(pq)
                cnt += 1
                if cnt == 3:
                    ans += p
                    cnt = 0
            result.append(f"{ans:d}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月20日 星期一

ZeroJudge 解題筆記:d133. 00357 - Let Me Count The Ways

作者:王一哲
日期:2026年4月20日


ZeroJudge 題目連結:d133. 00357 - Let Me Count The Ways

解題想法


因為每種面額的硬幣數量不限,可以取任意數量組成目標金額,是無限背包問題,用動態規畫解題。假設 $dp[i]$ 代表總金額為 $i$ 的組合方式,初始值為 $dp[0] = 1$,總金額 $0$ 只有 $1$ 種組合方式。用一層 for 迴圈依序取出各種面額 $coin$,再用一層 for 迴圈,依序更新 $i = coin$ 到 $i = 30000$ 的方法數,狀態轉移方式為 $dp[i] += dp[i-coin]$。建完 $i = 0$ 到 $i = 30000$ 的方法數表格之後,依序讀取測資、查表、輸出對應的答案。由於這題的方法數有點多,如果用 C 或 C++ 解題,$dp$ 表格要用 long,如果用 int 會溢位。

Python 程式碼


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

    maxn = 30000
    dp = [0]*(maxn + 1)
    dp[0] = 1
    for coin in (1, 5, 10, 25, 50):
        for i in range(coin, maxn+1):
            dp[i] += dp[i - coin]

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        m = dp[n]
        if m == 1:
            result.append(f"There is only 1 way to produce {n:d} cents change.\n")
        else:
            result.append(f"There are {m:d} ways to produce {n:d} cents change.\n")

    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月19日 星期日

ZeroJudge 解題筆記:d132. 00340 - Master-Mind Hints

作者:王一哲
日期:2026年4月19日


ZeroJudge 題目連結:d132. 00340 - Master-Mind Hints

解題想法


先讀取答案 $ans$,計算 $0$ 到 $9$ 的數量存入串列 $cntAns$。接下來讀取猜測的數字 $guess$,如果數字都是 $0$ 就中止迴圈;將 $cntAns$ 複製一份到串列 $cnt$,依序檢查 $ans, guess$ 的每個位數,找出位置、數字皆相等的數字並更新 $A$ 的值,如果數字對但位置錯則存入串列 $remain$,最後再從 $remain$ 的資料計算 $B$ 的值。

Python 程式碼


使用時間約為 21 ms,記憶體約為 8.8 MB,通過測試。
import sys

num = 0  # 計算第幾次遊戲用的計數器
for line in sys.stdin:  # 繼續執行直到沒有讀到字串為止
    if line == "0\n": break  # 如果讀到 0\n 結束 for 迴圈
    n = int(line.strip())  # 讀取答案的位數
    num += 1  # num 加 1
    print(f"Game {num:d}:")  # 印出遊戲次數
    ans = list(map(int, input().split()))  # 讀取答案
    cntAns = [0]*10  # 因為題目限制只有 1 ~ 9 的數字,用長度為 10 的串列計數即可
    for a in ans: cntAns[a] += 1  # 在外面跑一次就好
    for line2 in sys.stdin:  # 繼續讀取字串
        guess = list(map(int, line2.split()))  # 讀取猜測的數字
        if all(g == 0 for g in guess): break  # 如果猜的數字都是 0,結束這次的遊戲
        cnt = cntAns.copy()  # 等一下會被修改掉,複製一份到 cnt
        remain = []  # 用來記錄沒有對到正確位置的數字
        A, B = 0, 0  # 值及位置都正確數字數量,只有值正確但位置錯的數字數量
        for a, g in zip(ans, guess):  # 依序由 ans 及 guess 讀取資料 a, g
            if a == g:  # 如果 a 等於 g,值及位置都正確
                A += 1  # A 加 1
                cnt[a] -= 1  # a 對應的數量減 1
            elif cnt[g] > 0: remain.append(g)  # 如果值對但位置錯,將 g 加入 remain
        for r in remain:  # 依序由 remain 讀取資料 r
            if cnt[r] > 0:  # 如果 r 在 cnt 之中的數量大於 0 
                cnt[r] -= 1  # r 在 cnt 之中的數量減 1
                B += 1  # B 加 1
        print(f"    ({A:d},{B:d})")  # 印出答案


2026年4月18日 星期六

ZeroJudge 解題筆記:d130. 00138 - Street Numbers

作者:王一哲
日期:2026年4月18日


ZeroJudge 題目連結:d130. 00138 - Street Numbers

解題想法


這題的答案固定為
         6         8
        35        49
       204       288
      1189      1681
      6930      9800
     40391     57121
    235416    332928
   1372105   1940449
   7997214  11309768
  46611179  65918161
左側包含起點的和 $$ lsum = \frac{(1+k) \times k}{2} $$ 右側包含起點的和 $$ rsum = \frac{(k+n) \times (n-k+1)}{2} $$ 如果 $lsum = rsum$,則 $$ \begin{align*} & k(1+k) = (k+n)(n-k+1) \\ ~\Rightarrow~& k + k^2 = nk - k^2 + k + n^2 - nk + n \\ ~\Rightarrow~& n^2 + n - 2k^2 = 0 \\ ~\Rightarrow~& n = \frac{-1 \pm \sqrt{1 + 8k^2}}{2} \end{align*} $$ 因為 $n, k \in N$ 且 $n>k$,上式的答案只能取加號;$1 + 8k^2$ 是完全平方數,開根號減 1 之後是偶數。

Python 程式碼


這題我沒有寫出正常作法而且可以 AC 的 Python 程式碼,只有直接輸出答案的作弊版,使用時間約為 8 ms,記憶體約為 8.8 MB,通過測試。
import sys

result = ["         6         8\n",
          "        35        49\n",
          "       204       288\n",
          "      1189      1681\n",
          "      6930      9800\n",
          "     40391     57121\n",
          "    235416    332928\n",
          "   1372105   1940449\n",
          "   7997214  11309768\n",
          "  46611179  65918161\n"]
sys.stdout.write("".join(result))


2026年4月17日 星期五

ZeroJudge 解題筆記:d131. 00160 - Factors and Factorials

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


ZeroJudge 題目連結:d131. 00160 - Factors and Factorials

解題想法


題目要計算 $1!$ 到 $100!$ 每個數字之中,所有相乘的質數次方。可以先建 $1$ 到 $100$ 之間的質數表 $primes$。接下來用一個長度為 $101$ 的串列 $dp$,儲存 $1!$ 到 $100!$ 對應的答案,用字典格式儲存,key 為質數,value 為次方。由於 $i! = i \times (i-1)!$ ,更新 $dp[i]$ 時先複製 $dp[i-1]$ 的資料,再加上 $i$ 的質因數分解結果。最後再依序讀取測資,從 $dp$ 之中找對應的答案,排列成題目要求的格式並輸出。

Python 程式碼


使用時間約為 8 ms,記憶體約為 8.8 MB,通過測試。
def solve():
    import sys
    
    """ 1 ~ 100 之間的質數 """
    maxn = 100
    sieve = [True]*(maxn+1)
    sieve[0] = sieve[1] = False
    for i in range(2, int(maxn**0.5) + 1):
        if sieve[i]:
            for j in range(i*i, maxn+1, i):
                sieve[j] = False
    primes = [i for i in range(101) if sieve[i]]
    
    """ 建表,dp 的內容是 0 ~ 100 每個質因數對應的次方,字典格式 """
    dp = [{p:0 for p in primes} for _ in range(maxn+1)]
    for i in range(2, maxn+1):  # 依序測試 2 ~ 100
        dp[i] = dp[i-1].copy()  # 複製前一組的資料
        x = i  # 暫存 i 的值
        for p in primes:  # 依序讀取質數
            y = 0  # 要增加的次方
            while x%p == 0:  # 如果 p 可以整除 x
                y += 1  # y 加 1
                x //= p  # x 除以 p
            if y > 0:  # 如果 y 大於 0
                dp[i][p] += y  # 更新 dp[i][p] 對應的次方
            if x == 1: break  # 如果 x 等於 1,不需要再測試

    """ 讀取資料,從 dp 讀取質因數的次方,組合成答案 """
    result= []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        if n == 0: break
        cofs = list(dp[n].values())  # 取出各個質因數的次方組成 list
        while cofs[-1] == 0: cofs.pop()  # 移除最後面的 0
        res = f"{n:3d}! ="  # 答案的開頭
        for i, cof in enumerate(cofs, start=1):  # 依序讀取 cofs
            res += f"{cof:3d}"  # 每個次方佔 3 格
            if i%15 == 0 and i < len(cofs): res += "\n      "  # 每 15 個要換行並空 6 格,如果是最後一項不換行
        res += "\n"  # 最後再換行
        result.append(res)
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月16日 星期四

ZeroJudge 解題筆記:d129. 00136 - Ugly Numbers

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


ZeroJudge 題目連結:d129. 00136 - Ugly Numbers

解題想法


將找到的 ugly number 存入串列 $ugly$ 之中,再用 3 個變數 $i, j, k$ 分別儲存目前串列之中最後一個 $2, 3, 5$ 的倍數索引值。用 while 迴圈,執行到串列的長度等於 1500 為止,裡面再用 3 個 while 迴圈,更新 $i, j, k$ 的值,更新完之後再取 $ugly[i]*2, ugly[j]*3, ugly[k]*5$ 的最小值,就是新的 ugly number。

Python 程式碼


使用時間約為 7 ms,記憶體約為 8.4 MB,通過測試。
ugly = [1]
i, j, k = 0, 0, 0  # 2, 3, 5 的倍數在 ugly 之中的索引值
while len(ugly) < 1500:
    # 更新 i, j, k,直到對應的數位分別乘以 2, 3, 5 大於 ugly 最後一項為止
    while ugly[i]*2 <= ugly[-1]: i += 1
    while ugly[j]*3 <= ugly[-1]: j += 1
    while ugly[k]*5 <= ugly[-1]: k += 1
    ugly.append(min(ugly[i]*2, ugly[j]*3, ugly[k]*5))  # 取最小一項加到 ugly
print(f"The 1500'th ugly number is {ugly[-1]:d}.")


2026年4月15日 星期三

ZeroJudge 解題筆記:d121. 00583 - Prime Factors

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


ZeroJudge 題目連結:d121. 00583 - Prime Factors

解題想法


因為 $n$ 的上限為 $2^{31}$,質因數的上限為 $\sqrt{2^{31}} \approx 46340$,可以先建 $1$ 到 $46340$ 的質數表,然後再用試除法找質因數。不過這題最麻煩的地方,反而是在於要拼出正確的輸出格式。

Python 程式碼


使用時間約為 39 ms,記憶體約為 9.1 MB,通過測試。
def solve():
    import sys
    
    # 建質數表,1 ~ sqrt(2^31),大約 46340
    maxn = 46340
    sieve = [True]*(maxn + 1)
    sieve[0] = sieve[1] = False
    for i in range(2, int(maxn**0.5) + 1):
        if sieve[i]:
            for j in range(i*i, maxn + 1, i):
                sieve[j] = False
    primes = [i for i in range(maxn + 1) if sieve[i]]
    
    # 主要的解題過程
    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        if n == 0: break  # 停止運作的條件
        if n == 1:  # 特例
            result.append("1 = 1\n")
            continue
        factors = []  # 質因數
        m = n  # n 的質先存到 m
        if m < 0:  # 負數
            factors.append(-1)  # 先加入 -1
            m = -m  # 轉為正值
        # 找質因數,m 的值先存到 x
        x = m
        for p in primes:
            if p*p > x:  # 沒有更大的質因數,中止迴圈
                break
            while x%p == 0:
                factors.append(p)
                x //= p
        if x > 1: factors.append(x)
        # 組合答案
        res = " x ".join(map(str, factors))
        result.append(f"{n:d} = {res}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月14日 星期二

ZeroJudge 解題筆記:d120. 10699 - Count the factors

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


ZeroJudge 題目連結:d120. 10699 - Count the factors

解題想法


這題可以事先計算 1 到 1000000 對應的最小質因數,在找特定數字 $n$ 的質因數個數時會比較快,可以從目前的值跳到下一個質因數。如果直接用除的,逐一測試比目前數值 $x$ 還小的質因數,這樣也會過,只是速度慢一點。

Python 程式碼


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

    # 建表,1 ~ 1000000 之間每個數對應的最小質因數 smallest prime factor
    max_num = 1000000
    spf = [0]*(max_num + 1)
    for i in range(2, max_num + 1):
        if spf[i] == 0:  # i 是質數
            spf[i] = i
            if i*i <= max_num:
                for j in range(i*i, max_num + 1, i):
                    if spf[j] == 0:  # 如果 j 還沒有最小質因數,設定成 i
                        spf[j] = i
    # 主要的解題過程
    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        if n == 0: break  # 停止的條件
        if n == 1:  # 特例
            result.append("1 : 0\n")
            continue
        cnt = 0  # 質因數數量
        x = n  # n 的值存到 x
        while x > 1:  # 如果 x 還能分解繼續執行
            p = spf[x]  # 取出 x 的最小質因數
            cnt += 1
            while x%p == 0:  # 如果 p 可以整除 x 繼續執行
                x //= p  # x 除以 p
        result.append(f"{n:d} : {cnt:d}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()

2026年4月13日 星期一

ZeroJudge 解題筆記:d117. 10924 - Prime Words

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


ZeroJudge 題目連結:d117. 10924 - Prime Words

解題想法


因為題目的字元編號最大值為 $52$,字串長度最大值為 $20$,因此只要建 $0$ 到 $1040$ 之間的質數表就可以了。需要注意,這題的 $1$ 被當作質數,因為範例測資中的 a 答案為 "It is a prime word."。接下來讀取字串,計算各字元的編號加總,查質數表,輸出對應的答案。

Python 程式碼


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

    maxn = 1040
    sieve = [True]*(maxn+1)
    sieve[0] = False
    for i in range(2, int(maxn**0.5) + 1):
        if sieve[i]:
            for j in range(i*i, maxn+1, i):
                sieve[j] = False
    
    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        s = data[ptr]
        ptr += 1
        total = 0
        for c in s:
            if c.isupper():
                total += ord(c) - ord('A') + 27
            elif c.islower():
                total += ord(c) - ord('a') + 1
        result.append("It is a prime word.\n" if sieve[total] else "It is not a prime word.\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月12日 星期日

ZeroJudge 解題筆記:d111. 10110 - Light, more light

作者:王一哲
日期:2026年4月12日


ZeroJudge 題目連結:d111. 10110 - Light, more light

解題想法


這題考數學。第 $1$ 趟及第 $n$ 趟都會改變第 $n$ 盞燈的開關,效果抵消。只要找第 $2$ 趟到第 $(n-1)$ 趟之間改變第 $n$ 盞燈的開關次數 $cnt$,也就是找因數的數量,如果 $cnt$ 是奇數輸出 $yes$,反之輸出 $no$。可以用 for 迴圈找 $i = 2$ 到 $i = \sqrt{n}$ 之間有幾個 $n$ 的因數,如果 $i^2 \neq n$ 增加 $2$ 個因數,如果 $i^2 = n$ 增加 $1$ 個因數。所以這題可以直接用 $n$ 是否為完全平方數找答案。

Python 程式碼


找 $n$ 的因數數量,使用時間約為 14 ms,記憶體約為 8.1 MB,通過測試。
import sys

result = []
for line in sys.stdin:
    n = int(line)
    if n == 0: break
    factors = {1, n}
    for i in range(2, int(n**0.5)+1):
        if n%i == 0:
            factors.add(i)
            factors.add(n//i)
    result.append("yes\n" if len(factors)%2 == 1 else "no\n")
sys.stdout.write("".join(result))

檢查 $n$ 是否為完全平方數,使用時間約為 7 ms,記憶體約為 8.2 MB,通過測試。
import sys

result = []
for line in sys.stdin:
    n = int(line)
    if n == 0: break
    r = int(n**0.5)
    result.append("yes\n" if r*r == n else "no\n")
sys.stdout.write("".join(result))


2026年4月11日 星期六

ZeroJudge 解題筆記:d096. 00913 - Joana and the Odd Numbers

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


ZeroJudge 題目連結:d096. 00913 - Joana and the Odd Numbers

解題想法


這題考數學。假設最後一項有 $n$ 項,則題目問的是第 $i$ 列($i = \frac{n+1}{2}$),此列最後一項 $$ a = \frac{[1 + 1 + 2 \times(i-1)] \times i}{2} $$ 最後 $3$ 項相加為 $(2a -1) \times 3 - 6$。 這題的測資 $n$ 有點大,用 C++ 解題時用 int 會溢位。

Python 程式碼


使用時間約為 7 ms,記憶體約為 8.4 MB,通過測試。
import sys

result = []
for line in sys.stdin:
    n = int(line)  # 最後一列有 n 項
    i = (n+1) // 2  # 第 i 列
    a = (1 + 1 + 2*(i-1)) * i // 2  # 第 i 列的最後 1 項
    result.append(f"{(a*2 - 1)*3 - 6:d}\n")  # 第 i 列最後 3 項相加
sys.stdout.write("".join(result))


2026年4月10日 星期五

ZeroJudge 解題筆記:d095. 00579 - ClockHands

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


ZeroJudge 題目連結:d095. 00579 - ClockHands

解題想法


先計算時針與12點的夾角,由於時針每12小時轉360度,1小時轉30度,1分鐘轉0.5度,計算角度前先將小時 h 對 12 取餘數。再計算分針與12點的夾角,分針每60分鐘轉360時,1分鐘轉6度。再將兩個角度相減、取絕對值,如果絕對值大於180度,再將答案改成360度減掉絕對值。

Python 程式碼


使用時間約為 7 ms,記憶體約為 8.2 MB,通過測試。
import sys

result = []
for line in sys.stdin:
    if line.strip() == "0:00": break
    h, m = map(int, line.split(":"))
    a = h%12*30 + m/2
    b = m*6
    c = abs(a-b)
    if c > 180: c = 360 - c
    result.append(f"{c:.3f}\n")
sys.stdout.write("".join(result))


2026年4月9日 星期四

ZeroJudge 解題筆記:d094. 00478 - Points in Figures: Rectangles and Circles, and Triangles

作者:王一哲
日期:2026年4月9日


ZeroJudge 題目連結:d094. 00478 - Points in Figures: Rectangles and Circles, and Triangles

解題想法


這題是 d093. 00477 - Points in Figures: Rectangles and Circles 的加強版,除了長方形、圓形之外還多了三角形,可以用上一題的程式碼為基礎,再加上檢查點是否在三角形內的自訂函式。 假設平面上的三角形 ABC 頂點座標分別為 $(x_A, y_A), (x_B, y_B), (x_C, y_C)$,如果要判斷平面上的點 P $(x_P, y_P)$ 是否在三角形之中,P 點座標可以表示為 $$ P = uA + vB + wC $$ 解聯立後可得 $$ \begin{align*} u &= \frac{(x_B - x_A)(y_P - y_A) - (y_B - y_A)(x_P - x_A)}{(x_B - x_A)(y_C - y_A) - (y_B - y_A)(x_C - x_A)}\\ v &= \frac{(x_C - x_A)(y_P - y_A) - (y_C - y_A)(x_P - x_A)}{(x_C - x_A)(y_B - y_A) - (y_C - y_A)(x_B - x_A)} \\ w &= 1 - u - v & \end{align*} $$ 係數符合的條件為
  • $0 \leq u \leq 1$
  • $0 \leq v \leq 1$
  • $0 \leq w \leq 1$


Python 程式碼


使用時間約為 9 ms,記憶體約為 8.6 MB,通過測試。
import sys

def in_rectangle(x, y, x1, y1, x2, y2):
    """
    代入要測試的點座標 (x, y)
    長方形左上頂點座標 (x1, y1)
    長方形右下頂點座標 (x2, y2)
    判斷點是否在長方形內
    """
    if x1 < x < x2 and y1 > y > y2: return True
    return False

def in_circle(x, y, xc, yc, r):
    """
    代入要測試的點座標 (x, y)
    圓心座標 (xc, yc)、半徑 r
    判斷點是否在圓形內
    """
    if (x-xc)**2 + (y-yc)**2 < r**2: return True
    return False

def in_triangle(x, y, xA, yA, xB, yB, xC, yC):
    """
    代入要測試的點座標 (x, y)
    三角形頂點座標 (xA, yA), (xB, yB), (xC, yC)
    判斷點是否在三角形內
    """
    u = ((xB - xA)*(y - yA) - (yB - yA)*(x - xA)) / ((xB - xA)*(yC - yA) - (yB - yA)*(xC - xA))
    v = ((xC - xA)*(y - yA) - (yC - yA)*(x - xA)) / ((xC - xA)*(yB - yA) - (yC - yA)*(xB - xA))
    w = 1 - u - v
    if 0 <= u <= 1 and 0 <= v <= 1 and 0 <= w <= 1: return True
    return False

""" 讀取形狀 """
figures = [[]]
for line in sys.stdin:
    if line.strip() == "*": break
    data = line.split()
    if data[0] == "r":
        figures.append([0] + list(map(float, data[1:])))
    elif data[0] == "c":
        figures.append([1] + list(map(float, data[1:])))
    else:
        figures.append([2] + list(map(float, data[1:])))

""" 判斷點是否在圖形內並組合答案 """
result = []
i = 0
n = len(figures)
for line in sys.stdin:
    if line.strip() == "9999.9 9999.9": break
    i += 1
    state = False
    x, y = map(float, line.split())
    for j in range(1, n):
        if figures[j][0] == 0:
            if in_rectangle(x, y, figures[j][1], figures[j][2], figures[j][3], figures[j][4]):
                result.append(f"Point {i:d} is contained in figure {j:d}\n")
                state = True
        elif figures[j][0] == 1:
            if in_circle(x, y, figures[j][1], figures[j][2], figures[j][3]):
                result.append(f"Point {i:d} is contained in figure {j:d}\n")
                state = True
        else:
            if in_triangle(x, y, figures[j][1], figures[j][2], figures[j][3], figures[j][4], figures[j][5], figures[j][6]):
                result.append(f"Point {i:d} is contained in figure {j:d}\n")
                state = True
    if not state:
        result.append(f"Point {i:d} is not contained in any figure\n")
sys.stdout.write("".join(result))


2026年4月8日 星期三

ZeroJudge 解題筆記:d093. 00477 - Points in Figures: Rectangles and Circles

作者:王一哲
日期:2026年4月8日


ZeroJudge 題目連結:d093. 00477 - Points in Figures: Rectangles and Circles

解題想法


另外寫兩個自訂函式 in_rectangle 及 in_circle,分別用來檢查指定的座標是否在長方形或圓形之中。先儲存所有形狀的資料,讀取到檢查的點之後,依序檢查這個點在哪些編號的圖形之中並輸出答案。

Python 程式碼


使用時間約為 9 ms,記憶體約為 8.5 MB,通過測試。
import sys

def in_rectangle(x, y, x1, y1, x2, y2):
    """
    代入要測試的點座標 (x, y)
    長方形左上頂點座標 (x1, y1)
    長方形右下頂點座標 (x2, y2)
    判斷點是否在長方形內
    """
    if x1 < x < x2 and y1 > y > y2: return True
    return False

def in_circle(x, y, xc, yc, r):
    """
    代入要測試的點座標 (x, y)
    圓心座標 (xc, yc)、半徑 r
    判斷點是否在圓形內
    """
    if (x-xc)**2 + (y-yc)**2 < r**2: return True
    return False

""" 讀取形狀 """
figures = [[]]
for line in sys.stdin:
    if line.strip() == "*": break
    data = line.split()
    if data[0] == "r":
        figures.append([0] + list(map(float, data[1:])))
    elif data[0] == "c":
        figures.append([1] + list(map(float, data[1:])))

""" 判斷點是否在圖形內並組合答案 """
result = []
i = 0
n = len(figures)
for line in sys.stdin:
    if line.strip() == "9999.9 9999.9": break
    i += 1
    state = False
    x, y = map(float, line.split())
    for j in range(1, n):
        if figures[j][0] == 0:
            if in_rectangle(x, y, figures[j][1], figures[j][2], figures[j][3], figures[j][4]):
                result.append(f"Point {i:d} is contained in figure {j:d}\n")
                state = True
        elif figures[j][0] == 1:
            if in_circle(x, y, figures[j][1], figures[j][2], figures[j][3]):
                result.append(f"Point {i:d} is contained in figure {j:d}\n")
                state = True
    if not state:
        result.append(f"Point {i:d} is not contained in any figure\n")
sys.stdout.write("".join(result))


2026年4月7日 星期二

ZeroJudge 解題筆記:k652. 二元搜尋樹復原 (BST)

作者:王一哲
日期:2026年4月7日


ZeroJudge 題目連結:k652. 二元搜尋樹復原 (BST)
原題連結

解題想法


自訂類別 Node 及 BST,在 BST 之中用遞迴從根節點開始往下找插入新節點的位置,題目的輸出方式是中序走訪,但是同時要印出父節點的值。詳細的說明已經寫在程式碼的註解之中。

Python 程式碼


使用時間約為 0.6 s,記憶體約為 61.8 MB,通過測試。
class Node:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None

class BST:
    def __init__(self):
        self.root = None

    def insert(self, val, node = None):
        # 如果 node 為 None,設為 self.root,如果 self.root 是空的,新增 self.root
        if node is None:
            node = self.root
            if self.root is None:
                self.root = Node(val)
                return
        # 如果 val 較小,放到 node.left,如果 node.left 是空的,新增 node.left
        # 如果已經有 node.left,遞迴,試著放到 node.left 之下
        if val < node.val:
            if not node.left:
                node.left = Node(val)
            else:
                self.insert(val, node.left)
        # 如果 val 較大,放到 node.right,如果 node.right 是空的,新增 node.right
        # 如果已經有 node.right,遞迴,試著放到 node.right 之下
        else:
            if not node.right:
                node.right = Node(val)
            else:
                self.insert(val, node.right)

    def inorder(self, node = None, parent = -1):
        # 中序走訪,順序為 left, root, right
        # 如果 node 為 None,設為 self.root
        if node is None:
            node = self.root
        # 如果有 node.left,遞迴,代入 node.left 及 node.val
        if node.left:
            self.inorder(node.left, node.val)
        # 印出 node.val 及父節點的值;如果是根節點,parent 的值為 -1
        print(node.val, parent)
        # 如果有 node.right,遞迴,代入 node.right 及 node.val
        if node.right:
            self.inorder(node.right, node.val)

def solve():
    import sys

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        # 測資給的順序是後序走訪,最後一個是根節點,倒序加入 bst 之中
        arr = list(map(int, data[ptr:ptr+n]))
        ptr += n
        bst = BST()
        for u in arr[::-1]:
            bst.insert(u)
        bst.inorder()

if __name__ == "__main__":
    solve()


2026年4月6日 星期一

ZeroJudge 解題筆記:d088. 00127 - "Accordian" Patience

作者:王一哲
日期:2026年4月6日


ZeroJudge 題目連結:d088. 00127 - "Accordian" Patience

解題想法


這題的測資格式比較特別,如果用 Python 解題,最好用 sys.stdin.read().split() 一次讀取所有的測資,再用索引值分割,這樣會比較方便。儲存所有牌堆資料時,用可以改變長度、刪除資料的 list 及 vector 比較方便。用 while 迴圈檢查是否有可以合併的牌堆,直到沒有可以合併的牌堆為止。

Python 程式碼


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

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        # 停止條件
        if data[ptr] == "#": break
        # 讀取兩行測資組成牌堆
        piles = [[card] for card in data[ptr:ptr+52]]
        ptr += 52
        # 移動過程
        changed = True  # 是否有交換牌
        while changed:  # 如果有交換繼續執行
            changed = False  # 先設定為 False
            i = 1  # 從牌堆 1 開始檢查
            while i < len(piles):  # 如果 i 還沒有出界繼續執行
                # 先檢查牌堆 i 與 i-3 最上面的牌是否同花色或同數字
                if i >= 3 and (piles[i][-1][0] == piles[i-3][-1][0] or \
                               piles[i][-1][1] == piles[i-3][-1][1]):
                    piles[i-3].append(piles[i].pop())  # 牌堆 i 最上面移到牌堆 i-3
                    if not piles[i]: del piles[i]  # 如果牌堆 i 是空的,刪除
                    changed = True  # 有交換牌
                    break  # 中止迴圈
                # 檢查牌堆 i 與 i-1 最上面的牌是否同花色或同數字
                elif i >= 1 and (piles[i][-1][0] == piles[i-1][-1][0] or \
                                 piles[i][-1][1] == piles[i-1][-1][1]):
                    piles[i-1].append(piles[i].pop())  # 牌堆 i 最上面移到牌堆 i-1
                    if not piles[i]: del piles[i]  # 如果牌堆 i 是空的,刪除
                    changed = True  # 有交換牌
                    break  # 中止迴圈
                # 牌堆 i 不能交換,檢查下一張
                i += 1
        # 組合答案
        n = len(piles)  # 牌堆數量
        nums = [len(pile) for pile in piles]  # 各牌堆張數
        if n == 1:  # 只有一堆
            result.append(f"1 pile remaining: {nums[0]:d}\n")
        else:  # 不只一堆
            result.append(f"{n:d} piles remaining: " + " ".join(map(str, nums)) + "\n")
    # 輸出答案
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月5日 星期日

ZeroJudge 解題筆記:d087. 00107 - The Cat in the Hat

作者:王一哲
日期:2026年4月5日


ZeroJudge 題目連結:d087. 00107 - The Cat in the Hat

解題想法


寫一個自訂函式 check,輸入起始高度 h、倍率 n,計算總數、高度為 1 的數量、總高度。用 for 迴圈依序測試倍率 1 ~ H-1,用 check 檢查代入的倍率高度為1的貓的數目是否符合測資。

Python 程式碼


使用時間約為 7 ms,記憶體約為 8.4 MB,通過測試。
import sys

def check(h, n):  # 輸入起始高度、倍率
    total, curr, hsum = 1, 1, h  # 總數,目前的數量,總高度
    h //= 1+n  # 新的高度
    while h >= 1:  # 如果 h 大於等於 1 繼續執行
        curr *= n  # 更新數量為 n 倍
        total += curr  # 更新總數
        hsum += curr*h  # 更新總高度
        h //= 1+n  # 更新高度
    return total, curr, hsum  # 回傳總數、高度為 1 的數量、總高度
    
result = []
lines = sys.stdin.readlines()
for line in lines:
    H, M = map(int, line.split())  # 起始高度、高度為 1 的數量
    if H == 0 and M == 0: break  # 如果 H 與 M 皆等於 0,中止迴圈
    total, imin, hsum = H, 1, H  # 試算結果,總數、高度為 1 的數量、總高度
    for i in range(1, H):  # 測試倍率 1 ~ H-1
        total, imin, hsum = check(H, i)  # 回傳測試結果
        if imin == M: break  # 如果 imin 等於 M,找到答案,中止迴圈
    result.append(f"{total-imin:d} {hsum:d}\n")
sys.stdout.write("".join(result))

2026年4月4日 星期六

ZeroJudge 解題筆記:d057. 11494 - Queen

作者:王一哲
日期:2026年4月4日


ZeroJudge 題目連結:d057. 11494 - Queen

解題想法


可能的步數有 3 種:
  1. 不需要移動,回傳 0。
  2. 在同一列、同一欄、斜直線上,回傳 1。
  3. 其它狀況,走 2 步一定會走到,回傳 2。


Python 程式碼


使用時間約為 10 ms,記憶體約為 3.6 MB,通過測試。
import sys

def find_step(x1, y1, x2, y2):  # 輸入起點、終點找步數
    if x1 == x2 and y1 == y2: return 0  # 不需要移動,回傳 0
    if x1 == x2 or y1 == y2: return 1  # 在同一欄或列,回傳 1
    if x2 - x1 == y2 - y1:  # 右上、左下斜直線
        dx = x2 - x1  # x 方向位移
        dy = y2 - y1  # y 方向位移
        if 1 <= x1 + dy <= 8 and 1 <= y1 + dx <= 8:  # 沒出界
            return 1  # 回傳 1
    if x2 - x1 == y1 - y2:  # 左上、右下斜直線
        dx = x2 - x1  # x 方向位移
        dy = y2 - y1  # y 方向位移
        if 1 <= x1 - dy <= 8 and 1 <= y1 - dx <= 8:  # 沒出界
            return 1  # 回傳 1
    # 其它狀況,最多只要水平、垂直各走一步,回傳 2
    return 2

result = []
lines = sys.stdin.readlines()
for line in lines:
    if line.strip() == "0 0 0 0": break
    step = find_step(*map(int, line.split()))
    result.append(f"{step:d}\n")
sys.stdout.write("".join(result))


2026年4月3日 星期五

ZeroJudge 解題筆記:d056. 10013 - Super long sums

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


ZeroJudge 題目連結:d056. 10013 - Super long sums

解題想法


這題如果用 Python 解題,最好用串列 a、b 儲存超長整數,再用另一個串列 ans 儲存計算結,串列資料格式為字串。運算時由 a、b 最後面住前讀取資料,將計算結果用字串格式存入 ans,輸出答案時將 ans 反序輸出。如果用 C++ 解題,可以用字串或 vector 儲存超長整數,用字串格式好像速度快一點。

Python 程式碼


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

    result = []
    data = sys.stdin.read().split()
    ptr = 0
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        for _ in range(n):
            m = int(data[ptr])
            ptr += 1
            a, b = [], []
            for _ in range(m):
                c, d = map(int, data[ptr:ptr+2])
                ptr += 2
                a.append(c)
                b.append(d)
            ans = []
            carry = 0
            for i, j in zip(a[::-1], b[::-1]):
                k = int(i) + int(j) + carry
                if k >= 10:
                    carry = 1
                    ans.append(f"{k%10}")
                else:
                    carry = 0
                    ans.append(f"{k}")
            if carry == 1: ans.append("1")
            res = "".join(ans[::-1])
            if not result: result.append(f"{res}\n")
            else: result.append(f"\n{res}\n")
        sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()


2026年4月2日 星期四

ZeroJudge 解題筆記:d055. 11437 - Triangle Fun

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


ZeroJudge 題目連結:d055. 11437 - Triangle Fun

解題想法


數學題。分點公式是高中數學課程內容,假設要在兩個點 $A$、$B$ 找到另一個點 $C$,使 $\overline{AB} : \overline{CB} = m : n$,則 $$ C = \frac{n}{m+n} A + \frac{m}{m+n} B $$ 題目的提示中有由寫三角形三個頂點求面積的公式 $$ area = \frac{1}{2} \times \left | A_x B_y - A_y B_x + B_x C_y - C_x B_y + C_x A_y - C_y A_x \right | $$ 還要自己推導,由兩條不平行的直線端點求交點座標的公式。假設 $(x_1, y_1), (x_2, y_2)$ 是直線 $y = ax + b$ 的端點座標,$(x_3, y_3), (x_4, y_4)$ 是直線 $y = a'x + b'$ 的端點座標。將 $(x_1, y_1), (x_2, y_2)$ 代入 $y = ax + b$ 可得 $$ y_1 = ax_1 + b ~~~~~ y_2 = ax_2 + b $$ 利用加減消去法可得 $$ a = \frac{y_1 - y_2}{x_1 - x_2} ~~~~~ b = \frac{x_2 y_1 - x_1 y_2}{x_2 - x_1} $$ 同理可得 $$ a' = \frac{y_3 - y_4}{x_3 - x_4} ~~~~~ b' = \frac{x_4 y_3 - x_3 y_4}{x_4 - x_3} $$ 再求 $y = ax + b$ 與 $y = a'x + b'$ 的交點 $(x, y)$,利用加減消去法可得 $$ x = \frac{b' - b}{a - a'} ~~~~~ y = \frac{a'b - ab'}{a' - a} $$

Python 程式碼


使用時間約為 11 ms,記憶體約為 3 MB,通過測試。
def find_point(ax, ay, bx, by):
    """
    代入直線的兩個端點 A、B
    求與 A、B 距離比為 1:2 的座標
    """
    cx = (2*ax + bx) / 3
    cy = (2*ay + by) / 3
    return cx, cy

def intersection(x1, y1, x2, y2, x3, y3, x4, y4):
    """
    x1, y1, x2, y2 是一條直線 y = ax + b 的兩個端點
    x3, y3, x4, y5 是另一條直線 y = a'x + b' 的兩個端點
    兩條直線不平行,求直線的交點
    """
    a = (y1 - y2) / (x1 - x2)
    b = (x2*y1 - x1*y2) / (x2 - x1)
    ap = (y3 - y4) / (x3 - x4)
    bp = (x4*y3 - x3*y4) / (x4 - x3)
    x = (bp - b) / (a - ap)
    y = (ap*b - a*bp) / (ap - a)
    return x, y

def find_area(ax, ay, bx, by, cx, cy):
    """
    代入三角形 ABC 的三的頂點座標求面積
    """
    return abs(ax*by - ay*bx + bx*cy - by*cx + cx*ay - cy*ax)/2

t = int(input())
for _ in range(t):
    Ax, Ay, Bx, By, Cx, Cy = map(float, input().split())
    Dx, Dy = find_point(Bx, By, Cx, Cy)
    Ex, Ey = find_point(Cx, Cy, Ax, Ay)
    Fx, Fy = find_point(Ax, Ay, Bx, By)
    Px, Py = intersection(Bx, By, Ex, Ey, Ax, Ay, Dx, Dy)
    Qx, Qy = intersection(Bx, By, Ex, Ey, Cx, Cy, Fx, Fy)
    Rx, Ry = intersection(Ax, Ay, Dx, Dy, Cx, Cy, Fx, Fy)
    area = find_area(Px, Py, Qx, Qy, Rx, Ry)
    print(f"{round(area):d}")


2026年4月1日 星期三

ZeroJudge 解題筆記:d054. 11310 - DELIVERY DEBACLE

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


ZeroJudge 題目連結:d054. 11310 - DELIVERY DEBACLE

解題想法


這題考動態規畫,最難的地方是找遞迴關係式。$dp[n]$ 代表長度為 $n$ 時的排列方式數量,先列出前幾項:
  1. $dp[0] = 1$,沒有任何的蛋糕,只有 1 種方式。
  2. $dp[1] = 1$,只能放 2 個方形蛋糕,只有 1 種方式。
  3. $dp[2] = 4 + 1 = 5$,用 1 個 L 形蛋糕及 1 個方形蛋糕佔據其中 4 格,再用 2 個方形蛋糕佔據剩下的 2 格,因為 L 形蛋糕的組合可以旋轉,有 4 種方式;也可以用 6 個方形蛋糕填滿 6 格,1 種方式;加起來共有 5 種方式。
$n = 3$ 開始會出現另一種排列方式,用 2 個 L 形蛋糕佔據其中 6 格,有以下 2 種方式
OOX     OXX
OXX     OOX
因此 $dp[3]$ 的組成有:
  1. $dp[2] = 5$,用 2 個方形蛋糕佔據最後 2 格。
  2. $4 \times dp[1] = 4 \times 1 = 4$,用 1 個 L 形蛋糕及 1 個方形蛋糕佔據最後 4 格,因為這個組合可以旋轉,要乘以 4。
  3. $2 \times dp[0] = 2 \times 1 = 2$,用 2 個 L 形蛋糕佔據最後 6 格,這個組合有 2 種,要乘以 2。
$dp[3] = dp[2] + 4 \times dp[1] + 2 \times dp[0] = 5 + 4 \times 1 + 2 \times 1 = 11$ $dp[4]$ 的組成有:
  1. $dp[3] = 11$,用 2 個方形蛋糕佔據最後 2 格。
  2. $4 \times dp[2] = 4 \times 5 = 4$,用 1 個 L 形蛋糕及 1 個方形蛋糕佔據最後 4 格,因為這個組合可以旋轉,要乘以 4。
  3. $2 \times dp[1] = 2 \times 1 = 2$,用 2 個 L 形蛋糕佔據最後 6 格,這個組合有 2 種,要乘以 2。
  4. $dp[4] = dp[3] + 4 \times dp[2] + 2 \times dp[1] = 11 + 4 \times 5 + 2 \times 1 = 35$
遞迴關係式為 $$ dp[n] = dp[n-1] + 4 \times dp[n-2] + 2 \times dp[n-3] $$

Python 程式碼


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

    # 建表,n = 0 ~ 40
    dp = [1]*41
    dp[2] = 5
    for i in range(3, 41):
        dp[i] = dp[i-1] + 4*dp[i-2] + 2*dp[i-3]
    # 讀取資料並查表找答案
    result = []
    data = sys.stdin.read().split()
    ptr = 1
    while ptr < len(data):
        n = int(data[ptr])
        ptr += 1
        result.append(f"{dp[n]:d}\n")
    sys.stdout.write("".join(result))

if __name__ == "__main__":
    solve()