熱門文章

2026年2月21日 星期六

ZeroJudge 解題筆記:c084. 00275 - Expanding Fractions

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


ZeroJudge 題目連結:c084. 00275 - Expanding Fractions

解題想法


這題要依序處理每個位數相除的結果,如果無法整除時要將餘數乘以 10 再繼續往下除,同時記錄每個餘出現過的位置,直到先找循環節的終點為止。最後還要依照題目要求的格式輸出答案。

Python 程式碼


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

def divid(a, b):  # a < b, 計算 a/b
    dec_part = []  # 小數部分
    repeat_start = -1  # 循環小數起點
    rem = a  # 餘數,由於題目限制 a < b,初始值為 a
    rem_pos = dict()  # 用字典儲存出現過的餘數位置
    while rem != 0:  # 如果無法整除繼續執行
        if rem in rem_pos:  # 如果這個餘數已經出現過
            repeat_start = rem_pos[rem]  # 設定循環的起點
            break  # 中止迴圈
        rem_pos[rem] = len(dec_part)  # 記錄這個餘數的位置
        rem *= 10  # 將餘數放大為 10 倍
        dec_part.append(str(rem//b))  # 小數部分加一位
        rem %= b  # 新的餘數
    ans = "." + "".join(dec_part)  # 要輸出的答案
    for i in range(0, len(ans), 50):  # 逐行輸出,每行最多 50 個字元
        print(ans[i:i+50])
    if repeat_start == -1:  # 不循環
        print("This expansion terminates.")
    else:  # 循環
        print(f"The last {len(dec_part)-repeat_start:d} digits repeat forever.")

for line in sys.stdin:
    n, m = map(int, line.split())
    if n == 0 and m == 0: break
    divid(n, m)

如果題目改成要計算相除的小數並標示循環節,可以這樣寫。
import sys

def divid(a, b):  # a < b, 計算 a/b
    int_part = str(a//b)  # 整數部分
    rem = a%b  # 餘數
    """ 特例,可以整除,回傳整數部分 """
    if rem == 0: return int_part
    """ 繼續處理小數部分 """
    dec_part = []  # 小數部分
    repeat_start = -1  # 循環小數起點
    rem_pos = dict()  # 用字典儲存出現過的餘數位置
    while rem != 0:  # 如果無法整除繼續執行
        if rem in rem_pos:  # 如果這個餘數已經出現過
            repeat_start = rem_pos[rem]  # 設定循環的起點
            break  # 中止迴圈
        rem_pos[rem] = len(dec_part)  # 記錄這個餘數的位置
        rem *= 10  # 將餘數放大為 10 倍
        dec_part.append(str(rem//b))  # 小數部分加一位
        rem %= b  # 新的餘數
    if repeat_start == -1:  # 不循環
        return int_part + "." + "".join(dec_part)
    else:  # 循環,用 () 標示循環節
        return int_part + "." + "".join(dec_part[:repeat_start]) \
               + "(" + "".join(dec_part[repeat_start:]) + ")"

for line in sys.stdin:
    n, m = map(int, line.split())
    if n == 0 and m == 0: break
    print(divid(n, m))


2026年2月20日 星期五

ZeroJudge 解題筆記:c083. 00130 - Roman Roulette

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


ZeroJudge 題目連結:c083. 00130 - Roman Roulette

解題想法


這題的題目敘述很怪,試了很多種排法才弄懂題目的意思。為了簡化問題,我將人排成一列,順時鐘方向是向右,如果到了最後一位就從最左邊開始算。計算規則如下:
  1. 首先是題目要找的起點位置,在計算第一個受害者的位置時,是從起點的左邊那個人開始向右算 k 個人,不是從起點開始算。
  2. 計算埋葬者原來的位置時,是從受害者左邊那個人開始向右算 k 個人,將埋葬者的位置移動到受害者的位置。
  3. 計算下一個受害者的位置時,是從前一個受害者,也就是埋葬者所在位置左邊那個人開始向右算 k 個人。
  4. 如果 1 號死亡,可以中止迴圈,再找下一個起點。
  5. 如果存活人數剩下一個,而且這個人是 1 號,找到答案,回傳起點位置。
以範例測資 $n = 5, k = 2, i = 1$ 為例,起始及每一輪的狀態如下
起始: [1, 2, 3, 4, 5]
第1輪: 起點編號 5, 受害者編號 2, 埋葬者編號 4, 目前存活的人 [1, 4, 3, 5]
第2輪: 起點編號 4, 受害者編號 5, 埋葬者編號 4, 目前存活的人 [1,  3, 4]
第3輪: 起點編號 4, 受害者編號 3, 埋葬者編號 1, 目前存活的人 [1, 4]
第4輪: 起點編號 1, 受害者編號 1, 目前存活的人 [4]


Python 程式碼


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

def solve(n, k):  # 用串列模擬過程求解
    for i in range(1, n+1):  # 依序測試起點編號 1 ~ n
        persons = [j for j in range(1, n+1)]  # 編號 1 ~ n 的人
        pos, m = (i-2+n)%n, n  # 起點位置其實是 i 的左側,轉成索引值再減 1;存活人數 m
        alive = True  # 1 號是否存活
        while m > 1 and alive:  # 如果 m 大於 1 且 1 號還活著,繼續執行
            vpos = (pos+k)%m  # 受害者索引值
            victim = persons.pop(vpos)  # 從 persons 移除受害者
            m -= 1  # 存活人數減 1
            if victim == 1: alive = False  # 如果 1 號死亡,alive 改成 False
            if m == 1: break  # 如果 m 等於 1,中止迴圈
            bpos = (vpos-1+k)%m  # 埋葬者索引值
            burier = persons.pop(bpos)  # 從 persons 移除埋葬者
            if bpos < vpos:  # 如果埋葬者在受害者左側
                persons.insert(vpos-1, burier)  # vpos 減 1,於 persons 插入埋葬者
                pos = (vpos-1+m)%m  # 更新下一輪的起點索引值
            else:  # 如果埋葬者在受害者右側
                persons.insert(vpos, burier)  # 於 persons 插入埋葬者
                pos = (vpos+m)%m  # 更新下一輪的起點索引值
        if alive: return i  # 如果 1 號存活,回傳 i
    return -1  # 無解,回傳 -1,理論上不會用到

for line in sys.stdin:
    n, k = map(int, line.split())
    if n == 0 and k == 0: break
    print(solve(n, k))


2026年2月19日 星期四

ZeroJudge 解題筆記:c082. 00118 - Mutant Flatworld Expolrers

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


ZeroJudge 題目連結:c082. 00118 - Mutant Flatworld Expolrers

解題想法


這類走格子的題目,我通常會在地圖的周圍加上一圈地圖中原來沒有使用的字元當作邊界,如果走到的格子是這個字元就代表出界,這樣可以不需要檢查索引值的邊界值。
依照題目給的指令,依序更新位置及方向,如果碰掉邊界標示為已掉落,不需要執行之後的指令。如果指令全部執行完還沒有掉落,印出最後的位置。

Python 程式碼


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

ctod = {'N': 0, 'E': 1, 'S': 2, 'W': 3}
dtoc = ('N', 'E', 'S', 'W')
dx = (0, 1, 0, -1)  # 向東 +x,順序為 NESW
dy = (1, 0, -1, 0)  # 向北 +y,順序為 NESW

n, m = map(int, sys.stdin.readline().split())  # 地圖右上角頂點 (n, m)
# 地圖最右側、最上方加 -1 作為哨兵,0 為未標記,1 為已標記
# grid[y][x] 對應到地圖座標 (x, y)
grid = [[0]*(n+1) + [-1] for _ in range(m+1)]
grid.append([-1]*(n+2))

for line in sys.stdin:
    cmd = list(line.split())
    x, y, d = int(cmd[0]), int(cmd[1]), ctod[cmd[2]]  # 起點座標 (x, y),方向 d
    lost = False  # 是否掉落
    for c in sys.stdin.readline().rstrip():
        if c == 'R': d = (d+1)%4  # 向右轉90度
        elif c == 'L': d = (d-1+4)%4  # 向左轉90度
        else:  # 前進1格
            nx, ny = x+dx[d], y+dy[d]  # 測試 (ny, nx)
            if grid[ny][nx] == -1:  # (ny, nx) 出界
                if grid[y][x] == 0:  # (y, x) 未標記
                    print(f"{x:d} {y:d} {dtoc[d]:s} LOST")  # 最後的位置
                    grid[y][x] = 1  # 標記 (x, y)
                    lost = True  # 已掉落
                    break  # 中止迴圈,忽略之後的指令
                else: continue  # (y, x) 已標記,執行下一個指令
            else:  # (ny, nx) 未出界
                x, y = nx, ny  # 更新 x, y
    # 執行完指令,未掉落,印出位置及方向
    if not lost: print(f"{x:d} {y:d} {dtoc[d]:s}")


2026年2月18日 星期三

ZeroJudge 解題筆記:c081. 00102 - Ecological Bin Packing

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


ZeroJudge 題目連結:c081. 00102 - Ecological Bin Packing

解題想法


只有 6 種排列方式,分別為 BCG, BGC, CBG, CGB, GBC, GCB,依照這個順序分別計算對應的移動數量,取第一個移動數量最小值。

Python 程式碼


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

for line in sys.stdin:
    b1, g1, c1, b2, g2, c2, b3, g3, c3 = map(int, line.split())
    # 測試的排列順序為 BCG, BGC, CBG, CGB, GBC, GCB
    ans, imin = "", 1E9
    cnt1 = g1+c1+b2+g2+b3+c3
    if cnt1 < imin:
        ans = "BCG"; imin = cnt1
    cnt2 = g1+c1+b2+c2+b3+g3
    if cnt2 < imin:
        ans = "BGC"; imin = cnt2
    cnt3 = b1+g1+g2+c2+b3+c3
    if cnt3 < imin:
        ans = "CBG"; imin = cnt3
    cnt4 = b1+g1+b2+c2+g3+c3
    if cnt4 < imin:
        ans = "CGB"; imin = cnt4
    cnt5 = b1+c1+g2+c2+b3+g3
    if cnt5 < imin:
        ans = "GBC"; imin = cnt5
    cnt6 = b1+c1+b2+g2+g3+c3
    if cnt6 < imin:
        ans = "GCB"; imin = cnt6
    print(ans, imin)

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

for line in sys.stdin:
    b1, g1, c1, b2, g2, c2, b3, g3, c3 = map(int, line.split())
    # BCG
    cnt1 = g1+c1+b2+g2+b3+c3
    # BGC
    cnt2 = g1+c1+b2+c2+b3+g3
    # CBG
    cnt3 = b1+g1+g2+c2+b3+c3
    # CGB
    cnt4 = b1+g1+b2+c2+g3+c3
    # GBC
    cnt5 = b1+c1+g2+c2+b3+g3 
    # GCB
    cnt6 = b1+c1+b2+g2+g3+c3
    ans = sorted([(cnt1, "BCG"), (cnt2, "BGC"), (cnt3, "CBG"), (cnt4, "CGB"), (cnt5, "GBC"), (cnt6, "GCB")])
    print(ans[0][1], ans[0][0])


2026年2月17日 星期二

ZeroJudge 解題筆記:c077. 00417 - Word Index

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


ZeroJudge 題目連結:c077. 00417 - Word Index

解題想法


這題給的條件比較寬鬆,先建表列出所有答案再查表也能通過。但如果能夠找出規律,用數學解會快很多。

Python 程式碼


建表,使用時間約為 31 ms,記憶體約為 15.6 MB,通過測試。
import sys

cnt = 0  # 編號
table = dict()  # 表格,內容為{字串: 編號}
# a ~ z 的編號
for i in range(26):
    cnt += 1
    s = chr(i+ord('a'))
    table[s] = cnt
# ab ~ yz 的編號
for i in range(26):
    s = chr(i+ord('a'))
    for j in range(i+1, 26):
        t = s + chr(j+ord('a'))
        cnt += 1
        table[t] = cnt
# abc ~ xyz 的編號
for i in range(26):
    s = chr(i+ord('a'))
    for j in range(i+1, 26):
        t = s + chr(j+ord('a'))
        for k in range(j+1, 26):
            u = t + chr(k+ord('a'))
            cnt += 1
            table[u] = cnt
# abcd ~ wxyz 的編號
for i in range(26):
    s = chr(i+ord('a'))
    for j in range(i+1, 26):
        t = s + chr(j+ord('a'))
        for k in range(j+1, 26):
            u = t + chr(k+ord('a'))
            for x in range(k+1, 26):
                v = u + chr(x+ord('a'))
                cnt += 1
                table[v] = cnt
# abcde ~ vwxyz 的編號
for i in range(26):
    s = chr(i+ord('a'))
    for j in range(i+1, 26):
        t = s + chr(j+ord('a'))
        for k in range(j+1, 26):
            u = t + chr(k+ord('a'))
            for x in range(k+1, 26):
                v = u + chr(x+ord('a'))
                for y in range(x+1, 26):
                    w = v + chr(y+ord('a'))
                    cnt += 1
                    table[w] = cnt
# 讀取測資並查表輸出答案,如果 key 不在 table 之中則輸出 0
for line in sys.stdin:
    print(table.get(line.rstrip(), 0))

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

def comb(n, m):  # C n 取 m
    if m < 0 or m > n: return 0  # 不合法的算式,回傳 0
    m = min(m, n-m)  # 取 m, n-m 較小者
    ans = 1  # 組合數
    for i in range(1, m+1):
        ans = ans*(n-i+1)//i
    return ans

def calculate_code(s):  # 輸入字串,回傳編號
    n, code = len(s), 0  # 字串長度,編號
    # 檢查字符串是否合法,後面的字元大於前面的字元
    for i in range(n-1):
        if s[i+1] <= s[i]:  # 不合法,回傳 0
            return 0
    # 計算長度為 1 ~ n-1 的組合數
    for i in range(1, n):
        code += comb(26, i)
    # 計算長度 n 的排名
    for i in range(n):  # 掃過 s[0] ~ s[n-i]
        start = ord('a') if i == 0 else ord(s[i-1]) + 1  # i = 0 從 a 開始,否則從 s[i-1] 下一個開始
        end = ord(s[i])  # 到 s[i] 的編號為止
        for c in range(start, end):
            code += comb(26 - (c-ord('a')+1), n-i-1)
    # 最後要再加 1
    return code + 1

for line in sys.stdin:
    print(calculate_code(line.rstrip()))


2026年2月16日 星期一

ZeroJudge 解題筆記:c074. 00441 - Lotto

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


ZeroJudge 題目連結:c074. 00441 - Lotto

解題想法


這題可以用遞迴窮舉所有的組合。但如果用 Python 解題,可以用 itertools.combinations 枚舉所有指定數據中取出 6 個的組合,速度比遞迴還快。

Python 程式碼


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

def solve(cand, arr, depth):
    if len(arr) == depth:
        print(*arr)
        return
    for i in range(len(cand)):
        arr.append(cand[i])
        solve(cand[i+1:], arr, depth)
        arr.pop()
        
first = True
for line in sys.stdin:
    if line.rstrip() == "0": break
    if not first: print()
    first = False
    nums = list(map(int, line.split()))[1:]
    solve(nums, [], 6)

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

result = []
data = sys.stdin.read().split()
ptr = 0
while ptr < len(data):
    n = int(data[ptr])
    ptr += 1
    if n == 0: break
    nums = sorted(map(int, data[ptr:ptr+n]))
    ptr += n
    if result: result.append("\n")
    for comb in combinations(nums, 6):
        res = " ".join(map(str, comb))
        result.append(f"{res}\n")
sys.stdout.write("".join(result))


2026年2月15日 星期日

ZeroJudge 解題筆記:c073. 00101 - The Blocks Problem

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


ZeroJudge 題目連結:c073. 00101 - The Blocks Problem

解題想法


指令有以下 5 種,其中 a 和 b 是積木的編號:
  1. move a onto b: 先將 a 和 b 上方的積木放回原來的位置,再將 a 移到 b 上方。
  2. move a over b: 先將 a 上方的積木放回原來的位罝,b 所在的那堆積木不動,再將 a 移到 b 上方。
  3. pile a onto b: 先將 b 上方的積木放回原位,再將 a 本身及上方的積木一起放到 b 上方。
  4. pile a over b: 將 a 本身和上方的積木一起搬到到 b 所在的那堆積木上方。
  5. quit: 動作結束,印出結果。
先畫出範例測資 1 的操作過程再寫程式碼,會比較清楚要怎麼寫。範例測資 1,$n = 10$,初始及每次操作後的狀態分別為
initial: [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]
move 9 onto 1: [[0], [1, 9], [2], [3], [4], [5], [6], [7], [8], []]
move 8 over 1: [[0], [1, 9, 8], [2], [3], [4], [5], [6], [7], [], []]
move 7 over 1: [[0], [1, 9, 8, 7], [2], [3], [4], [5], [6], [], [], []]
move 6 over 1: [[0], [1, 9, 8, 7, 6], [2], [3], [4], [5], [], [], [], []]
pile 8 over 6: [[0], [1, 9, 8, 7, 6], [2], [3], [4], [5], [], [], [], []]  # 8, 6 在同一疊,無效
pile 8 over 5: [[0], [1, 9], [2], [3], [4], [5, 8, 7, 6], [], [], [], []]
move 2 over 1: [[0], [1, 9, 2], [], [3], [4], [5, 8, 7, 6], [], [], [], []]
move 4 over 9: [[0], [1, 9, 2, 4], [], [3], [], [5, 8, 7, 6], [], [], [], []]

範例測資 2,$n = 4$,初始及每次操作後的狀態分別為
initial: [[0], [1], [2], [3]]
pile 0 over 1: [[], [1, 0], [2], [3]]
pile 2 over 3: [[], [1, 0], [], [3, 2]]
move 1 onto 3: [[0], [], [2], [3, 1]]


Python 程式碼


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

for line in sys.stdin:
    n = int(line)
    piles = [[i] for i in range(n)]  # 每一疊的木塊編號
    pos = [i for i in range(n)]  # 每個編號的木塊各在第幾疊
    for line in sys.stdin:
        if line.rstrip() == "quit":
            for i, pile in enumerate(piles):
                print(f"{i:d}: ", end="")
                print(*pile)
            break
        command = list(line.split())
        a, b = int(command[1]), int(command[3])
        if pos[a] == pos[b]: continue  # a, b 在同一疊,不合法的指令
        if command[0] == "move":
            if command[2] == "onto":
                while piles[pos[a]][-1] != a:  # 檢查 a 所在的那一疊上方
                    c = piles[pos[a]].pop()  # 取出並移除最上方的木塊編號為 c
                    pos[c] = c  # 把 c 歸位
                    piles[c].append(c)
                while piles[pos[b]][-1] != b:  # 檢查 b 所在的那一疊上方
                    c = piles[pos[b]].pop()  # 取出並移除最上方的木塊編號為 c
                    pos[c] = c  # 把 c 歸位
                    piles[c].append(c)
                piles[pos[b]].append(piles[pos[a]].pop())  # 將 a 移到 b 所在那一疊上方
                pos[a] = pos[b]  # a 的位置改成 b 所在的位置
            elif command[2] == "over":
                while piles[pos[a]][-1] != a:  # 檢查 a 所在的那一疊上方
                    c = piles[pos[a]].pop()  # 取出並移除最上方的木塊編號為 c
                    pos[c] = c  # 把 c 歸位
                    piles[c].append(c)
                piles[pos[b]].append(piles[pos[a]].pop())  # 將 a 移到 b 所在那一疊上方
                pos[a] = pos[b]  # a 的位置改成 b 所在的位置
        elif command[0] == "pile":
            if command[2] == "onto":
                while piles[pos[b]][-1] != b:  # 檢查 b 所在的那一疊上方
                    c = piles[pos[b]].pop()  # 取出並移除最上方的木塊編號為 c
                    pos[c] = c  # 把 c 歸位
                idx = piles[pos[a]].index(a)  # 找出 a 在這一疊的索引值
                sub = piles[pos[a]][idx:]  # 取出 idx 之後的項目
                piles[pos[a]] = piles[pos[a]][:idx]  # 移除 idx 之後的項目
                for d in sub:  # 修改 a 及上方積木的位置
                    pos[d] = pos[b]  # 改成 b 所在的位置
                piles[pos[b]] += sub  # 將 a 及上方的積木移到 b 所在那一疊上方
            elif command[2] == "over":
                idx = piles[pos[a]].index(a)  # 找出 a 在這一疊的索引值
                sub = piles[pos[a]][idx:]  # 取出 idx 之後的項目
                piles[pos[a]] = piles[pos[a]][:idx]  # 移除 idx 之後的項目
                for d in sub:  # 修改 a 及上方積木的位置
                    pos[d] = pos[b]  # 改成 b 所在的位置
                piles[pos[b]] += sub  # 將 a 及上方的積木移到 b 所在那一疊上方


2026年2月14日 星期六

ZeroJudge 解題筆記:c069. 00602 - What Day Is It?

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


ZeroJudge 題目連結:c069. 00602 - What Day Is It?

解題想法


將題目需要的功能分拆成以下的自訂函式
  1. is_leap_julian,用來判斷依據儒略曆閏年規則,輸入的年份是否為閏年。
  2. is_leap_gregorian,用來判斷依據格里曆閏年規則,輸入的年份是否為閏年。
  3. is_valid_date,檢查日期是否有效。
  4. get_day_of_week,計算指定日期是星期幾。


Python 程式碼


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

# 月份名稱對照表
month_names = ("", "January", "February", "March", "April", "May", "June",
               "July", "August", "September", "October", "November", "December")
# 每個月的天數 (非閏年)
month_days = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
# 星期名稱
weekday_names = ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")

def is_leap_julian(year):
    """ 儒略曆閏年規則:能被4整除 """
    return year % 4 == 0

def is_leap_gregorian(year):
    """ 格里曆閏年規則:能被4整除且不被100整除,或者能被400整除 """
    return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

def is_valid_date(month, day, year):
    """ 檢查日期是否有效 """
    # 月份小於 1 或大於 12,日期小於 1,年小於 1
    if month < 1 or month > 12 or day < 1 or year < 1: return False
    # 特殊情況:1752年9月的轉換,沒有9月3日 ~ 13日
    if year == 1752 and month == 9:
        if day == 2 or day == 14: return True
        if 2 < day < 14: return False
    # 該月份的天數
    max_day = month_days[month]
    if month == 2:  # 2月,是否為閏年
        if year < 1752:  # 採用儒略曆
            leap = is_leap_julian(year)
        else:  # 採用格里曆
            leap = is_leap_gregorian(year)
        if leap:  # 如果是閏年,有29天
            max_day = 29
    # 如果 day 小於等於 max_day 為 True,反之為 False
    return day <= max_day

def get_day_of_week(month, day, year):
    """計算指定日期是星期幾"""
    # 題目給定公元1年1月1日是星期六 (Sunday ~ Saturday 的編號分別為 0 ~ 6)
    total_days = 0  # 指定日期為公元1年1月1日之後第幾天
    # 計算從1年到(year-1)年的總天數
    for y in range(1, year):
        if y < 1752:
            leap = is_leap_julian(y)
        else:
            leap = is_leap_gregorian(y)
        total_days += 366 if leap else 365
    # 計算當年1月1日到當月1日的天數
    for m in range(1, month):
        if m == 2:
            if year < 1752:
                leap = is_leap_julian(year)
            else:
                leap = is_leap_gregorian(year)
            total_days += 29 if leap else 28
        else:
            total_days += month_days[m]
    # 加上當月的天數
    total_days += day - 1
    # 特殊調整:1752年9月轉換,橫跨1752年9月14日,要減11天
    if year > 1752 or (year == 1752 and month > 9) or (year == 1752 and month == 9 and day >= 14):
        total_days -= 11
    # 計算星期幾 (1年1月1日是星期六,即weekday=6)
    weekday = (total_days + 6) % 7
    return weekday

""" 主要的解題過程 """
for line in sys.stdin:
    parts = line.strip().split()
    if len(parts) != 3: continue  # 不合法的資料,理論上不會遇到
    month, day, year = map(int, parts)  # 月、日、年轉成整數
    if month == 0 and day == 0 and year == 0: break  # 中止迴圈的條件
    # 檢查日期是否有效
    if not is_valid_date(month, day, year):
        print(f"{month}/{day}/{year} is an invalid date.")
        continue
    # 獲取星期幾
    print(f"{month_names[month]} {day}, {year} is a {weekday_names[get_day_of_week(month, day, year)]}")


2026年2月13日 星期五

ZeroJudge 解題筆記:c061. 00530 - Binomial Showdown

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


ZeroJudge 題目連結:c061. 00530 - Binomial Showdown

解題想法


要用計算階乘的對稱性減少計算次數。

Python 程式碼


因為 ZeroJudge 網站大約在2025年10月更新到 Python 3.8.10,可以使用 math.comb 處理這題。使用時間約為 7 ms,記憶體約為 2.8 MB,通過測試。
import sys, math

for line in sys.stdin:
    n, m = map(int, line.split())
    if n == 0 and m == 0: break
    print(math.comb(n, m))

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

for line in sys.stdin:
    n, m = map(int, line.split())
    if n == 0 and m == 0: break
    ans = 1
    m = min(m, n-m)
    for i in range(1, m+1): ans = ans*(n-m+i)//i
    print(ans)


2026年2月12日 星期四

ZeroJudge 解題筆記:c060. 00392 - Polynomial Showdown

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


ZeroJudge 題目連結:c060. 00392 - Polynomial Showdown

解題想法


這題要考慮各種顯示的格式,很麻煩。

Python 程式碼


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

# 各次方要顯示的變數格式
orders = ("", "x", "x^2", "x^3", "x^4", "x^5", "x^6", "x^7", "x^8")

for line in sys.stdin:
    cofs = list(map(int, line.split()))  # 8 ~ 0 次方的係數
    if all(cof == 0 for cof in cofs):  # 特例,如果所有的係數都是 0
        print("0")  # 印出 0
        continue  # 找下一行
    """ 處理一般的狀況 """
    res = []  # 儲存結果用的串列
    for i, cof in enumerate(cofs):  # 依序讀取係數
        p = 8-i  # 次方
        if cof == 0: continue  # 係數是 0,找下一位
        if cof > 1:  # 係數大於 1
            if res:  # 如果 res 已經有內容,前面要有 " + "
                res.append(f" + {cof:d}{orders[p]:s}")
            else:  # 反之,前面不需要有 " + ",只要有係數、變數、次方
                res.append(f"{cof:d}{orders[p]:s}")
        elif cof == 1:  # 係數等於 1
            if res:  # 如果 res 已經有內容,前面要有 " + "
                if p == 0: res.append(" + 1")  # 常數項,只要有 " + 1"
                else: res.append(f" + {orders[p]:s}")  # 其它項,只要有 " + 變數、次方"
            else:  # 反之,前面不需要有 " + "
                if p == 0: res.append("1")  # 常數項,只要有 "1"
                else: res.append(f"{orders[p]:s}")  # 其它項,只要有變數、次方
        elif cof == -1:  # 係數等於 -1
            if res:  # 如果 res 已經有內容,前面要有 " - "
                if p == 0 res.append(" - 1")  # 常數項,只要有 " - 1"
                else: res.append(f" - {orders[p]:s}")  # 其它項,只要有 " - 變數、次方"
            else:  # 反之,前面不需要有 " - "
                if p == 0: res.append("-1")  # 常數項,只要有 "-1"
                else: res.append(f"-{orders[p]:s}")  # 其它項,只要有 "-變數、次方"
        elif cof < -1:  # 係數小於 -1
            if res:  # 如果 res 已經有內容,前面要有 " - "
                res.append(f" - {-cof:d}{orders[p]:s}")
            else:  # 反之,前面不需要有 " - ",只要有係數、變數、次方
                res.append(f"{cof:d}{orders[p]:s}")
    print("".join(res))  # 將 res 接成字串再輸出


2026年2月11日 星期三

ZeroJudge 解題筆記:c054. 10082 - WERTYU

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


ZeroJudge 題目連結:c054. 10082 - WERTYU

解題想法


因為轉換的關係沒有編碼上的規律,用字典建表比較方便。

Python 程式碼


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

table = {'1': '`', '2': '1', '3': '2', '4': '3', '5': '4', '6': '5',
         '7': '6', '8': '7', '9': '8', '0': '9', '-': '0', '=': '-',
         'W': 'Q', 'E': 'W', 'R': 'E', 'T': 'R', 'Y': 'T', 'U': 'Y',
         'I': 'U', 'O': 'I', 'P': 'O', '[': 'P', ']': '[', '\\': ']',
         'S': 'A', 'D': 'S', 'F': 'D', 'G': 'F', 'H': 'G', 'J': 'H',
         'K': 'J', 'L': 'K', ';': 'L', '\'': ';',
         'X': 'Z', 'C': 'X', 'V': 'C', 'B': 'V', 'N': 'B',
         'M': 'N', ',': 'M', '.': ',', '/': '.', ' ': ' '}

for line in sys.stdin:
    s = line.rstrip()
    t = []
    for c in s: t.append(table.get(c, ' '))
    print("".join(t))


2026年2月10日 星期二

ZeroJudge 解題筆記:c050. 00453 - Goldbach's Conjecture

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


ZeroJudge 題目連結:c050. 00453 - Goldbach's Conjecture

解題想法


先建立 1000000 以內的質數表 primes,但是不包含 2,因為題目要測試的數字大於 4。如果要測試的數字為 $n$,先用二分搜尋法從表格中找到最接近且大於等於 $n$ 的質數索引值 $idx$,接下來從 3 開始往上找到 $primes[idx]$ 為止,最早找到的解就是差值最大的解。

Python 程式碼


使用時間約為 1 s,記憶體約為 25.5 MB,通過測試。
import sys, bisect

### 建立 1 ~ 1000000 的質數表,不包含 2 ###
maxn = 1000000
sieve = [True]*(maxn+1)
sieve[0] = sieve[1] = False
for i in range(0, maxn+1, 2): sieve[i] = False
for i in range(3, 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
    idx = bisect.bisect_left(primes, n)
    a, b = 0, 0
    for x in primes[:idx+1]:  # 從 3 開始往上找,最早找到的解就是差值最大的解
        y = n-x
        idy = bisect.bisect_left(primes, y)
        if primes[idy] == y:
            a, b = x, y
            break
    if a == 0 and b == 0:
        reesult.append("Goldbach's conjecture is wrong.\n")
    else:
        result.append(f"{n:d} = {a:d} + {b:d}\n")
sys.stdout.write("".join(result))


2026年2月9日 星期一

ZeroJudge 解題筆記:c049. 00356 - Square Pegs And Round Holes

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


ZeroJudge 題目連結:c049. 00356 - Square Pegs And Round Holes

解題想法


先算數學,如果正方形的邊長為 $n$,則圓的半徑為 $r = 0.5 \times (2n - 1)$。接下來用兩層 for 迴圈掃過正方形的右上角每一格,假設正方形正中央為原點 $(0, 0$),格子座標的左下角 $(x, y)$,如果 $(x+1)^2 + (y+1)^2 \leq r^2$ 代表這格在圓之中,如果 $x^2 + y^2 > r^2$ 代表這格在圓之外,再將計算結果乘以 4 就是對應的答案 $inner$ 及 $outer$,圓上的格子數則是 $4n^2 - outer - inner$。

Python 程式碼


使用時間約為 1.2 s,記憶體約為 3.3 MB,通過測試。
import sys

first = True
for line in sys.stdin:
    if not first: print()
    first = False
    n = int(line)
    r = (2*n - 1) * 0.5
    inner, outer = 0, 0
    for x in range(n):
        for y in range(n):
            if (x+1)**2 + (y+1)**2 <= r*r: inner += 1
            elif x**2 + y**2 > r*r: outer += 1
    inner *= 4; outer *= 4
    print(f"In the case n = {n:d}, {4*n*n-outer-inner:d} cells contain segments of the circle.")
    print(f"There are {inner:d} cells completely contained in the circle.")

2026年2月8日 星期日

ZeroJudge 解題筆記:c048. 10161 - Ant on a Chessboard

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


ZeroJudge 題目連結:c048. 10161 - Ant on a Chessboard

解題想法


觀察位置的規律,第 1 格在左下角,第 4 格在邊長 $2 \times 2$ 正方形右下角,第 9 格在邊長 $3 \times 3$ 正方形左上角,第 16 格在邊長 $4 \times 4$ 正方形右下角,第 25 格在邊長 $5 \times 5$ 正方形左下角,第 36 格在邊長 $6 \times 6$ 正方形右下角。可以先建表,再用二分搜尋法找 n 在第幾列。也可以直接開根號,向上取整數。

Python 程式碼


使用時間約為 29 ms,記憶體約為 5.2 MB,通過測試。
import sys, bisect

maxn = 44722  # 平方為 2000057284,超出題目上限 2000000000
nums = [i*i for i in range(maxn+1)]  # i 平方的數字,開頭放 0,用二分搜尋法時比較方便
for line in sys.stdin:
    n = int(line)  # 時間 n
    if n == 0: break
    m = bisect.bisect_left(nums, n)  # n 在 nums 之中的棋盤的第 m 列
    b = nums[m] - n  # n 是 nums 第 m 列往回數 b 格
    if m%2 == 1:  # 如果 m 是奇數,先向上,再向左
        if b < m: print(b+1, m)  # 倒過來向右數
        else: print(m, m+m-b-1)  # 倒過來向下數
    else:  # 如果 m 是偶數,先向右,再向下
        if b < m: print(m, b+1)  # 倒過來向上數
        else: print(m+m-b-1, m)  # 倒過來向左數

2026年2月7日 星期六

ZeroJudge 解題筆記:c045. 00490 - Rotating Sentences

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


ZeroJudge 題目連結:c045. 00490 - Rotating Sentences

解題想法


這題我在 Python 是用二維串列儲存字串內容,在 C++ 則是用 vector 加 string 儲存內容。先讀取所有的測資,接下來用二層 for 迴圈讀取測資中的字元,外層處理欄位,內容從最後一列讀取到第 0 列。輸出答案時,如果這一列不是整整空白才要輸出。

Python 程式碼


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

lines = sys.stdin.readlines()  # 一次讀取所有的資測,包含結尾的 \n
n = len(lines)  # 共有 n 列
ans = [[] for _ in range(100)]  # 答案,題目限制每列最多 100 個字元
for c in range(100):  # 欄位,題目限制每列最多 100 個字元
    for r in range(n-1, -1, -1):  # 依序讀取第 n-1 到 0 列
        m = len(lines[r]) - 1
        if c < m: ans[c].append(lines[r][c])  # 如果 r 沒有出界,lines[r][c] 加入 ans[c]
        else: ans[c].append(" ")  # 反之,空格加入 ans[c]
for a in ans:  # 依序讀取 ans 每一列的資料 a
    row = "".join(a)  # 將 a 組成字串 row
    if not row.isspace():  # 如果 row 不是整列空白
        sys.stdout.write(row + "\n")


2026年2月6日 星期五

ZeroJudge 解題筆記:c044. 10008 - What's Cryptanalysis

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


ZeroJudge 題目連結:c044. 10008 - What's Cryptanalysis

解題想法


這題可以用表格或字典計數,以下的寫法採用表格計數,用一個長度為 26 的陣列記錄每個字母出現的次數,不是英文字母的字元不需要計數。計數完畢之後,再將次數、字母組成 tuple 或 pair 存入陣列之中,先依照次數由大到小排序,如果次數相同再依照字母由小到大排序。

Python 程式碼


使用時間約為 21 ms,記憶體約為 3.3 MB,通過測試。
n = int(input())
cnt = [0]*26
for _ in range(n):
    s = input()
    for c in s:
        if c.isupper():
            cnt[ord(c) - ord('A')] += 1
        elif c.islower():
            cnt[ord(c) - ord('a')] += 1
ans = [(v, chr(k+ord('A'))) for k, v in enumerate(cnt) if v > 0]
ans.sort(key = lambda x : (-x[0], ord(x[1])))
for a in ans: print(a[1], a[0])


2026年2月5日 星期四

ZeroJudge 解題筆記:c039. 00100 - The 3n + 1 problem

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


ZeroJudge 題目連結:c039. 00100 - The 3n + 1 problem

解題想法


用一個自訂函式 cycle 處理數值 n 的循環次數,寫起來會比較方便。函式內部按照題目的要求,分別處理 n 為奇數、偶數兩種狀況,更新 n 的數值。

Python 程式碼


使用時間約為 2.3 s,記憶體約為 3.3 MB,通過測試。
import sys

def cycle(n):
    times = 1  # 如果 n 等於 1,至少有 1 次
    while n != 1:
        times += 1
        if n%2 == 1: n = 3*n + 1
        else: n //= 2
    return times

for line in sys.stdin:
    a, b = map(int, line.split())
    print(f"{a:d} {b:d} ", end="")
    if a > b: a, b = b, a
    imax = 0
    for i in range(a, b+1):
        imax = max(imax, cycle(i))
    print(f"{imax:d}")


2026年2月4日 星期三

ZeroJudge 解題筆記:c036. 00573 - The Snail

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


ZeroJudge 題目連結:c036. 00573 - The Snail

解題想法


用 $y$ 儲存目前的高度,預設值為 0。用一個無窮迴圈,不斷更新每天的高度,更新的順序為
  1. 白天往上爬高度 $u$,如果 $y > h$ 印出 success on day {days},中止迴圈。
  2. 晚上向下滑高度 $d$,如果 $y < 0$ 印出 failure on day {days},中止迴圈。
  3. 更新下一天向上爬的高度 $u$,如果 $u < 0$ 時歸零。


Python 程式碼


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

for line in sys.stdin:
    if line.rstrip() == "0 0 0 0": break
    h, u, d, f = map(float, line.split())
    days = 0
    dec = u*f*0.01
    y = 0.0
    while True:
        days += 1
        y += u
        if y > h:
            print(f"success on day {days:d}")
            break
        y -= d
        if y < 0:
            print(f"failure on day {days:d}")
            break
        u -= dec
        if u < 0: u = 0.0


2026年2月3日 星期二

ZeroJudge 解題筆記:c034. 00424 - Integer Inquiry

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


ZeroJudge 題目連結:c034. 00424 - Integer Inquiry

解題想法


Python 支援大數運算,直接加起來就好。C++ 則要用字串儲存數字,然後從最後一位往前加。

Python 程式碼


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

isum = 0
for line in sys.stdin:
    n = int(line)
    if n == 0: break
    isum += n
print(isum)


C++ 程式碼


使用時間約為 4 ms,記憶體約為 336 kB,通過測試。
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

string big_sum(string s, string t) {  // 輸出字串 s, t,輸出相加後的字串 u
    string u = "";  // 儲存相加的結果
    int m = (int)s.size(), n = (int)t.size();  // s, t 的長度
    if (m > n) {  // s 較長,在 t 前面補 0
        string zeros (m-n, '0');
        t = zeros + t;
    } else if (m < n) {  // t 較長,在 s 前面補 0
        string zeros (n-m, '0');
        s = zeros + s;
    }
    int mn = max(m, n), carry = 0;  // 最大長度 mn,進位數值只會是 0 或 1
    for(int i=mn-1; i>=0; i--) {  // 由最後一位向前讀取
        char a = s[i], b = t[i];  // 字元 a, b
        int c = (a-'0') + (b-'0') + carry;  // a, b 相加後的結果 c
        u = char(c%10 + '0') + u;  // 更新 u
        carry = c/10;  // 更新 carry
    }
    if (carry == 1) u = '1' + u;  // 如果 carry 等於 1,在 u 前面補 1
    for(int i=0; i<(int)u.size(); i++) {  // 刪除 u 前面的 0
        if (u[i] != '0') {
            u = u.substr(i);
            break;
        }
    }
    return u;
}

int main() {
    ios::sync_with_stdio(0); cin.tie(0);
    string s, t;
    getline(cin, s);
    while(getline(cin, t)) s = big_sum(s, t);
    cout << s << "\n";
    return 0;
}


2026年2月2日 星期一

ZeroJudge 解題筆記:c033. 00406 - Prime Cuts

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


ZeroJudge 題目連結:c033. 00406 - Prime Cuts

解題想法


這個題目的 1 被視為質數。質數表最大值到 1009,因為測資的 n 最大值為 1000,如果只建到 1000,在質數表中用二分搜尋法找 n 可能會出界。

Python 程式碼


使用時間約為 19 ms,記憶體約為 3.3 MB,通過測試。
import sys, bisect

nums = [1]
state = [True]*1010
for i in range(2, 1010):
    if not state[i]: continue
    nums.append(i)
    for j in range(i+i, 1010, i):
        state[j] = False

first = True
for line in sys.stdin:
    n, c = map(int, line.split())
    if not first: print()
    first = False
    idx = bisect.bisect_left(nums, n)
    if nums[idx] == n: k = idx + 1
    else: k = idx
    if (k%2 == 0 and 2*c >= k) or \
       (k%2 == 1 and 2*c + 1 >= k):
        sub = nums[:k]
    elif k%2 == 0: sub = nums[k//2 - c : k//2 + c]
    else: sub = nums[(k+1)//2 - c : k//2 + c]
    print(f"{n:d} {c:d}: ", end="")
    print(*sub)


2026年2月1日 星期日

ZeroJudge 解題筆記:c032. 00382 - Perfection

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


ZeroJudge 題目連結:c032. 00382 - Perfection

解題想法


注意,實際上有多行測資。我是用試除法找 $n$ 的因數,將所有的因數儲存在集合之中。最後再比較所有的因數加總 $isum$ 與 $n$ 的大小,輸出對應的答案。

Python 程式碼


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

def test(n):
    if n == 1: return "DEFICIENT"
    factors = {1}
    for i in range(2, int(n**0.5)+1):
        if n%i == 0:
            factors.add(i)
            factors.add(n//i)
    isum = sum(factors)
    if isum == n:
        return "PERFECT"
    elif isum < n:
        return "DEFICIENT"
    else:
        return "ABUNDANT"

print("PERFECTION OUTPUT")
for line in sys.stdin:
    for n in map(int, line.split()):
        if n == 0: continue
        print(f"{n:5d}  {test(n):s}")
print("END OF OUTPUT")