熱門文章

2026年3月2日 星期一

ZeroJudge 解題筆記:c096. 00700 - Date Bugs

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


ZeroJudge 題目連結:c096. 00700 - Date Bugs

解題想法


我只有想到最暴力的作法,用集合 ans 儲存所有第一部電腦所有可能的年份,再依序計算其它電腦可能的年份存入集合 cand,取 ans 與 cand 的交集更新 ans。如果最後 ans 有資料,答案是 ans 之中的最小值;反之則輸出 **Unknown bugs detected.**。這個寫法不快,但是容易操作。

Python 程式碼


暴力解,使用時間約為 23 ms,記憶體約為 6 MB,通過測試。
import sys

ca = 0
maxy = 10000  # 答案上限為 9999
for line in sys.stdin:
    if line.rstrip() == "0": break;
    ca += 1
    if ca > 1: print()  # 如果不是第一筆測資,先印出空行
    n = int(line)  # 電腦數量
    ans = set()  # 第 1 部電腦可能對應的年份
    yi, ai, bi = map(int, sys.stdin.readline().split())  # 顯示年份 yi,於 bi 年出問題,出問題之後回到 ai
    di = bi - ai  # 從 bi 回到 ai 的差值
    for i in range(0, maxy-yi, di): ans.add(yi+i) # 計算可能的年份差
    for _ in range(n-1):  # 讀取 n-1 行資料
        cand = set()  # 第 2 ~ n 部電腦可能對應的年份
        y, a, b = map(int, sys.stdin.readline().split())  # 顯示年份 y,於 b 年出問題,出問題之後回到 a
        d = b - a  # 從 b 回到 a 的差值
        for i in range(0, maxy-y, d): cand.add(y+i)  # 計算可能的年份差
        ans.intersection_update(cand)  # 更新 ans
    print(f"Case #{ca:d}:")
    if ans:  # 如果有交集,印出 ans 之中的最小值
        print(f"The actual year is {min(ans):d}.")
    else:  # 反之,印出 Unknown bugs detected.
        print("Unknown bugs detected.")


2026年3月1日 星期日

ZeroJudge 解題筆記:c094. 00661 - Blowing Fuses

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


ZeroJudge 題目連結:c094. 00661 - Blowing Fuses

解題想法


用陣列記錄每個電器是否開啟,依序讀取操作的電器編號,更新電器使用狀態及總電流。用一個布林值記錄是否已經燒掉,如果總電流過大標記為 True。

Python 程式碼


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

ca = 0  # 第幾次測試
for line in sys.stdin:
    n, m, c = map(int, line.split())  # n 個電器,m 次操作,最大電流 c
    if n == 0 and m == 0 and c == 0: break  # 中止迴圈的條件
    ca += 1  # ca 加 1
    if ca > 1: print()  # 如果不是第 1 次測試,先印出空白列
    current = [0] + [int(sys.stdin.readline()) for _ in range(n)]  # 讀取 n 個電器的電流,開頭加 0
    blown = False  # 是否已燒掉
    curr, imax = 0, 0  # 目前的電流,電流最大值
    turn_on = [False]*(n+1)  # 電器狀態,預設為關閉
    for _ in range(m):  # 讀取 m 次操作
        idx = int(sys.stdin.readline())  # 電器編號
        if blown: continue  # 如果已燒掉,繼續讀完剩下的操作
        turn_on[idx] = not turn_on[idx]  # 更新電器的狀態
        if turn_on[idx]: curr += current[idx]  # 如果開啟,加上這個電器的電流
        else: curr -= current[idx]  # 如果關閉,減去這個電器的電流
        if curr > c: blown = True  # 如果 curr 大於 c,已燒掉
        else: imax = max(imax, curr)  # 反之,更新最大電流
    print(f"Sequence {ca:d}")  # 印出答案
    if blown:
        print("Fuse was blown.")
    else:
        print("Fuse was not blown.")
        print(f"Maximal power consumption was {imax:d} amperes.")


2026年2月28日 星期六

ZeroJudge 解題筆記:c093. 00608 - Counterfeit Dollar

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


ZeroJudge 題目連結:c093. 00608 - Counterfeit Dollar

解題想法


這題的困難之處在於分析測量結果,詳情請參考下方程式碼之中的註解。我是用集合儲存硬幣資料,由於 Python 的集合可以直接取聯集 (union)、差集 (difference),寫起來比較簡單。

Python 程式碼


使用時間約為 7 ms,記憶體約為 3 MB,通過測試。
T = int(input())  # T 組測資
for _ in range(T):
    """ 前置作業 """
    data = []  # 測試資料,內容為 [left, right, satatus]
    coins = set()  # 所有參與測試的硬幣
    status = dict()  # 硬幣狀態,內容為 name: [light possible, heavy possible]
    for _ in range(3):  # 讀取 3 次測試結果
        left, right, state = input().split()  # 左側硬幣,右側硬幣,右側狀態
        data.append([left, right, state])  # 資料加到 data
        for coin in left: coins.add(coin)  # 更新 coins
        for coin in right: coins.add(coin)
    for coin in coins:  # 更新 status,預設值為 [True, True]
        status[coin] = [True, True]
    """ 處理測試資料 """
    for left, right, state in data:  # 依序讀取測試資料
        if state == "even":  # 如果兩側平衡,左、右的硬幣都是真的
            for coin in left: status[coin] = [False, False]  # 更新 status,兩種狀態都是 False
            for coin in right: status[coin] = [False, False]
        elif state == "up":  # 如果右側較高,代表左側較重、右側較輕,假的硬幣一定在這些硬幣之中
            leri = set(left).union(set(right))  # 兩側硬幣的聯集
            for coin in coins.difference(leri):  # 沒有參與這次測試的硬幣都是真的
                status[coin] = [False, False]
            for coin in left: status[coin][0] = False  # 左側硬幣不可能比真的硬幣輕
            for coin in right: status[coin][1] = False  # 右側硬幣不可能比真的硬幣重
        else:  # 如果右側較低,代表左側較輕、右側較重,假的硬幣一定在這些硬幣之中
            leri = set(left).union(set(right))  # 兩側硬幣的聯集
            for coin in coins.difference(leri):  # 沒有參與這次測試的硬幣都是真的
                status[coin] = [False, False]
            for coin in left: status[coin][1] = False  # 左側硬幣不可能比真的硬幣重
            for coin in right: status[coin][0] = False  # 右側硬幣不可能比真的硬幣輕
    """ 結束測試,找答案 """
    fake, result = "", ""  # 假的硬幣名稱、狀態
    for coin, (light, heavy) in status.items():  # 依序讀取 status 的資料
        if light and not heavy:  # 如果這個硬幣可能比較輕、不可能比較重
            fake = coin  # 找到假的硬幣
            result = "light"  # 較輕
            break  # 中止迴圈
        if not light and heavy:  # 如果這個硬幣不可能比較輕、可能比較重
            fake = coin  # 找到假的硬幣
            result = "heavy"  # 較重
            break  # 中止迴圈
    print(f"{fake:s} is the counterfeit coin and it is {result:s}.")


2026年2月27日 星期五

ZeroJudge 解題筆記:c092. 00587 - There's treasure everywhere!

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


ZeroJudge 題目連結:c092. 00587 - There's treasure everywhere!

解題想法


我先用字典儲存各種方向縮寫對應的 1 個單位位移量,接下來再依照讀取到的測資計算東西向、南北向位移。我花最多時間的地方反而是在讀取測資,因為用數量不一定的逗號、句號分隔資料,反而比用空格分隔資料還要麻煩。用 C++ 解題時,如果用 float 會吃 WA,要用 double 才能 AC。

Python 程式碼


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

ca = 0
for line in sys.stdin:
    if line.rstrip() == "END": break
    ca += 1  # case 加 1
    if ca > 1: print()
    x, y = 0.0, 0.0  # 目前的位置
    dia = 1.0/(2**0.5)  # 對角線長度,2分之根號2
    dirs = {"N": (0.0, 1.0), "E": (1.0, 0.0), "S": (0.0, -1.0), "W": (-1.0, 0.0),
            "NE": (dia, dia), "NW": (-dia, dia), "SE": (dia, -dia), "SW": (-dia, -dia)}
    steps = line[:-2].split(",")  # 刪除最後的 .\n,再用 , 分割,存成串列
    for step in steps:  # 依序讀取每一步的距離、方向
        a = 0; d = ""  # 移動距離、方向
        for i, c in enumerate(step):  # 依序讀取 step 的字元,讀到英文字母為止
            if c.isalpha():
                a = int(step[:i])
                d = step[i:]
                break
        x, y = x + a*dirs[d][0], y + a*dirs[d][1]  # 更新位置
    print(f"Map #{ca:d}")
    print(f"The treasure is located at ({x:.3f},{y:.3f}).")
    print(f"The distance to the treasure is {(x*x + y*y)**0.5:.3f}.")


2026年2月26日 星期四

ZeroJudge 解題筆記:c091. 00576 - Hiaku Review

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


ZeroJudge 題目連結:c091. 00576 - Hiaku Review

解題想法


Python 可以用 split("/") 以斜線分割句字,而 C++ 則是依序讀取字元,讀到 / 時結算前一個句字。依序計算每個句子中的音節數量,但是連續的母音只能算 1 個音節,記錄前方的字元否為母音,如果這個字元不是母音,則音節數量加 1。

Python 程式碼


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

for line in sys.stdin:
    if line.rstrip() == "e/o/i": break
    sens = line.rstrip().split("/")
    standard = (0, 5, 7, 5)  # 三個句子的標準音節數量
    haiku = True  # 是否符合規則
    for i, sen in enumerate(sens, start=1):
        cnt = 0  # 音節數量
        state = False  # 前方的字元是否為母音 aeiouy
        for c in sen:  # 依序讀取 sen 的字元 c
            if c in "aeiouy": state = True  # 如果 c 是 aeiouy
            else:  # 如果 c 不是 aeiouy
                if state: cnt += 1  # 如果前方的字元是母音,音節數量加 1
                state = False  # 重設狀態
        if state: cnt += 1  # 如果最後是連續母音,cnt 加 1
        if cnt != standard[i]:  # 如果音節數量不合
            print(i)  # 印出 i
            haiku = False  # 不符合規則
            break  # 中止迴圈
    if haiku: print("Y")  # 如果符合規則,印出 Y


2026年2月25日 星期三

ZeroJudge 解題筆記:c089. 00389 - Basically Speaking

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


ZeroJudge 題目連結:c089. 00389 - Basically Speaking

解題想法


由於 Python 的 int 內建了用指定進位制將字串轉換成 10 進位整數的功能,這部分可以使用預設工具;但是輸出格式之中沒有對應 7 進位制的工具,需要自己寫。而 C++ 則是兩個部分都要自己寫。

Python 程式碼


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

def ntos(n, base):  # 輸入整數 n、基底 base,輸出轉換後的字串
    # 數字 0 ~ 15 對應的字元
    table = {0: '0', 1: '1', 2: '2', 3: '3',
             4: '4', 5: '5', 6: '6', 7: '7',
             8: '8', 9: '9', 10: 'A', 11: 'B',
             12: 'C', 13: 'D', 14: 'E', 15: 'F'}
    s = ""  # 儲存結果用的字串
    while n > 0:  # 如果 n 大於 0 繼續執行
        s = table[n%base] + s  # 取 n%base 對應的字元加到 s 前面
        n //= base  # n 變為 1/base 倍
    return s  # 回傳 s

for line in sys.stdin:
    arr = line.split()  # 分割後會轉為串列存到 arr
    # 先將字串用指定的基底 arr[1] 轉成 10 進位整數,再用 ntos 轉成 arr[2] 進位字串
    s = ntos(int(arr[0], int(arr[1])), int(arr[2]))
    m = len(s)  # s 的長度
    if m > 7: print("  ERROR")  # 長度大於 7,印出  ERROR
    else: print(" "*(7-m) + s)  # 反之前面補空格到長度為 7 再輸出


2026年2月24日 星期二

ZeroJudge 解題筆記:c088. 00516 - Prime Land

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


ZeroJudge 題目連結:c088. 00516 - Prime Land

解題想法


讀取一行測資,再每次讀取測資中的兩個數字,分別為底數及次方,將這個數字 $n$ 還原成 10 進位;接下來對 $n-1$ 做質因數分解,依照題目要求的格式輸出答案。

Python 程式碼


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

for line in sys.stdin:
    if line.rstrip() == "0": break
    arr = list(map(int, line.split()))  # 讀取一行資料
    n, m = 1, len(arr)  # n 儲存測資對應的整數,測資長度 m
    for i in range(0, m, 2):  # 一次讀取測資中的兩個數字
        n *= arr[i]**arr[i+1]  # 計算 n
    n -= 1  # 題目要算 n-1 的質因數分解結果
    curr = n  # 儲存 n-1,之後的計算會改變量值
    ans = []  # 答案
    for i in range(2, int(n**0.5)+1):  # 測試 2 ~ sqrt(n) 的質因數
        p = 0  # i 的次方
        while curr%i == 0:  # 如果 curr 能被 i 整除
            p += 1  # 次方加 1
            curr //= i  # curr 變為 1/i 倍
        if p > 0: ans += [p, i]  # 如果 p 大於 0,ans 加上 [p, i]
    if curr > 1: ans += [1, curr]  # 如果 curr 大於 1,ans 要加上 [1, curr]
    if not ans: print(n, 1)  # 如果 ans 是空的,n 是因數,輸出 n 1
    else: print(*ans[::-1])  # 題目要求因數由大到小輸出


2026年2月23日 星期一

ZeroJudge 解題筆記:c087. 00412 - Pi

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


ZeroJudge 題目連結:c087. 00412 - Pi

解題想法


如果測資提供 $n$ 個數字,所有的數對共有 $$ tot = C_n^2 = \frac{n(n-1)}{2} $$ 如果其中有 $cnt$ 組互質,則 $$ \frac{6}{\pi^2} = \frac{cnt}{tot} ~\Rightarrow~ \pi = \sqrt{\frac{6 \times tot}{cnt}} $$

Python 程式碼


用 itertools.combinations 産生數對組合,使用時間約為 7 ms,記憶體約為 2.9 MB,通過測試。
import sys, itertools, math

for line in sys.stdin:
    n = int(line)
    if n == 0: break
    nums = [int(sys.stdin.readline()) for _ in range(n)]
    cnt, tot = 0, n*(n-1)//2
    for a, b in itertools.combinations(nums, 2):
        if math.gcd(a, b) == 1: cnt += 1
    if cnt == 0:
        print("No estimate for this data set.")
    else:
        print(f"{(6*tot/cnt)**0.5:.6f}")

用遞迴枚舉所有的數對組合,使用時間約為 7 ms,記憶體約為 2.9 MB,通過測試。
import sys, math

def comb(nums, arr, cnt):
    if len(arr) == 2:
        if math.gcd(*arr) == 1: return cnt+1
        return cnt
    for i in range(len(nums)):
        arr.append(nums[i])
        cnt = comb(nums[i+1:], arr, cnt)
        arr.pop()
    return cnt

for line in sys.stdin:
    n = int(line)
    if n == 0: break
    nums = [int(sys.stdin.readline()) for _ in range(n)]
    tot = n*(n-1)//2
    cnt = comb(nums, [], 0)
    if cnt == 0:
        print("No estimate for this data set.")
    else:
        print(f"{(6*tot/cnt)**0.5:.6f}")


2026年2月22日 星期日

ZeroJudge 解題筆記:c086. 00402 - M*A*S*H

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


ZeroJudge 題目連結:c086. 00402 - M*A*S*H

解題想法


很像約瑟夫環的題目,但是測資不大,直接模擬淘汰的過程即可。

Python 程式碼


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

ca = 0  # 第幾筆測資
for line in sys.stdin:
    ca += 1  # 加1
    if ca > 1: print()  # 如果不是第1筆測資,換行
    arr = list(map(int, line.split()))  # 讀取一行資料
    n, x = arr[0], arr[1]  # n 個人參加,x 個人可以放假
    persons = [i for i in range(1, n+1)]  # 産生 1 ~ n 的編號
    for k in arr[2:]:  # 依序讀取 20 張牌的數字
        if n == x: break  # 如果剩下 x 個人,中止迴圈
        idx = k-1  # 淘汰者的索引值,初始值為 k-1
        while n > x and idx < n:  # 如果人數大於 x 而且 idx 還沒有出界
            persons.pop(idx)  # 淘汰索引值為 idx 的人
            n -= 1  # 人數減 1
            idx += k-1  # 下一個索引值加 k-1,因為淘汰一個人,不是加 k
    print(f"Selection #{ca:d}")  # 印出答案
    print(*persons)

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

result = []
data = sys.stdin.read().split()
ptr = 0
ca = 0  # 第幾筆測資
while ptr < len(data):
    ca += 1  # 加1
    if ca > 1: result.append("\n")  # 如果不是第1筆測資,換行
    n = int(data[ptr])  # n 個人參加
    x = int(data[ptr+1])  # x 個人可以放假
    ptr += 2
    persons = list(range(1, n+1))  # 産生 1 ~ n 的編號
    cards = list(map(int, data[ptr:ptr+20]))
    ptr += 20
    for k in cards:  # 依序讀取 20 張牌的數字
        if n == x: break  # 如果剩下 x 個人,中止迴圈
        idx = k-1  # 淘汰者的索引值,初始值為 k-1
        while n > x and idx < n:  # 如果人數大於 x 而且 idx 還沒有出界
            persons.pop(idx)  # 淘汰索引值為 idx 的人
            n -= 1  # 人數減 1
            idx += k-1  # 下一個索引值加 k-1,因為淘汰一個人,不是加 k
    result.append(f"Selection #{ca:d}\n")  # 印出答案
    res = " ".join(map(str, persons))
    result.append(f"{res}\n")
sys.stdout.write("".join(result))


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")


2026年1月31日 星期六

ZeroJudge 解題筆記:c031. 00264 - Count on Cantor

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


ZeroJudge 題目連結:c031. 00264 - Count on Cantor

解題想法


先觀察數列的特性,第 $i$ 列共有 $i$ 項,數值為 $1/i, 2/(i-1), \dots, (i-1)/2, i/1$,第 $1$ 到 $i$ 列總數 $$ total = 1 + 2 + \dots + i = \frac{i(i+1)}{2} $$ 因此,我先建一個陣列 $nums$ 儲存第 0 列到第 $i$ 列的項目數量,直到數量大於等於 $10^7$ 為止。讀取測資 $n$ 之後,再用二分搜尋法從 $nums$ 之中找 $n$ 的索引值 $m$。若第 $n$ 項為 $p/q$,由於題目的數列順序為 S 形,如果 m 是奇數,代表取數值方向往右上,則 $p = nums[m] - n +1, q = m - p +1$;反之,方向往左下。則 $q = nums[m] - n +1, p = m - p +1$。

Python 程式碼


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

nums = [0]
i, tot = 0, 0
while tot < 1E7:
    i += 1
    tot += i
    nums.append(tot)

for line in sys.stdin:
    n = int(line)
    m = bisect.bisect_left(nums, n)
    if m%2 == 1:
        p = nums[m] - n + 1
        q = m - p + 1
    else:
        q = nums[m] - n + 1
        p = m - q + 1
    print(f"TERM {n:d} IS {p:d}/{q:d}")


2026年1月30日 星期五

ZeroJudge 解題筆記:c024. 10079 - Pizza Cutting

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


ZeroJudge 題目連結:c024. 10079 - Pizza Cutting

解題想法


這題困難之處在於數學證明。假設切 $n$ 刀的最大分割數量為 $m$ 塊,先列出前幾項的數量 1. $n = 0, m = 1$ 2. $n = 1, m = 1 + 1 = 2$ 3. $n = 2, m = 2 + 2 = 4$ 4. $n = 3, m = 3 + 4 = 7$ 5. $n = 4, m = 4 + 7 = 11$ 如果要使分割數量最多,最後一刀要與前面每一刀都交會。若 $dp[n]$ 代表切 $n$ 刀分割的最大數量,由以上的式子可以看出 $$ \begin{align*} dp[n] &= dp[0] + dp[n-1] + n\\ &= 1 + 1 + 2 + \dots + (n-1) + n\\ &= 1 + \frac{n(n+1)}{2} \\ &= \frac{n^2 + n + 2}{2} \end{align*} $$

Python 程式碼


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

for line in sys.stdin:
    n = int(line)
    if n < 0: break
    print((n*n + n + 2) // 2)


2026年1月29日 星期四

ZeroJudge 解題筆記:c012. 10062 - Tell me the frequencies!

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


ZeroJudge 題目連結:c012. 10062 - Tell me the frequencies!

解題想法


這題如果用 Python 解題,可以用預設的 map 或是 collections 函式庫中的 Counter 計數,用 Counter 比較方便,只要寫 Counter(字串) 就可以算出字串每個字元的個數。測資會有空白,如果用 C++ 要用 getline 讀取資料,但是不能計算到結尾的 \n 或 \r。

Python 程式碼


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

first = True
for line in sys.stdin:
    if not first: print()
    first = False
    cnt = Counter(line.strip())
    ans = [(v, ord(k)) for k, v in cnt.items()]
    ans.sort(key = lambda x : (x[0], -x[1]))
    for a in ans: print(a[1], a[0])

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

result = []
lines = sys.stdin.readlines()
for line in lines:
    if result: result.append("\n")
    cnt = Counter(line.strip())
    ans = [(v, ord(k)) for k, v in cnt.items()]
    ans.sort(key = lambda x : (x[0], -x[1]))
    for a in ans: result.append(f"{a[1]:d} {a[0]:d}\n")
sys.stdout.write("".join(result))


2026年1月28日 星期三

ZeroJudge 解題筆記:c014. 10035 - Primary Arithmetic

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


ZeroJudge 題目連結:c014. 10035 - Primary Arithmetic

解題想法


這題只要依照直式加法的作法,從最後一位開始相加,如果最後一個分別為 a, b,目前的進位值為 carry,如果 $a + b + carry \geq 10$ 需要進位,進位次數 $cnt$ 加 1,同時 $carry$ 重設為 1;反之不需要進位,$carry$ 重設為 0。因為測資的數字在 10 位數以內,可以直接用整數處理就好;但如果數字很大,需要用字串格式才能處理。

Python 程式碼


整數格式,使用時間約為 22 ms,記憶體約為 2.9 MB,通過測試。
import sys

for line in sys.stdin:
    n, m = map(int, line.split())
    if n == 0 and m == 0: break
    cnt, carry = 0, 0
    while n > 0 or m > 0:
        a, b = n%10, m%10
        if a + b + carry >= 10:
            cnt += 1
            carry = 1
        else:
            carry = 0
        n //= 10
        m //= 10
    if cnt == 0:
        print("No carry operation.")
    elif cnt == 1:
        print("1 carry operation.")
    else:
        print(f"{cnt:d} carry operations.")

字串格式,使用時間約為 25 ms,記憶體約為 2.9 MB,通過測試。
import sys

for line in sys.stdin:
    s, t = line.split()
    if s == "0" and t == "0": break
    n, m = len(s), len(t)  # 長度
    # 前面補 0 讓 s, t 長度相等
    if n < m:
        s = s.zfill(m)
    else:
        t = t.zfill(n)
    # 由後往前計算加法及進位
    cnt, carry = 0, 0  # 進位次數,目前進位的值
    for a, b in zip(s[::-1], t[::-1]):
        if int(a) + int(b) + carry >= 10:
            cnt += 1
            carry = 1
        else:
            carry = 0
    # 輸出對應的答案
    if cnt == 0:
        print("No carry operation.")
    elif cnt == 1:
        print("1 carry operation.")
    else:
        print(f"{cnt:d} carry operations.")


2026年1月27日 星期二

ZeroJudge 解題筆記:c012. 10062 - Tell me the frequencies!

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


ZeroJudge 題目連結:c012. 10062 - Tell me the frequencies!

解題想法


這題如果用 Python 解題,可以用預設的 map 或是 collections 函式庫中的 Counter 計數,用 Counter 比較方便,只要寫 Counter(字串) 就可以算出字串每個字元的個數。測資會有空白,如果用 C++ 要用 getline 讀取資料,但是不能計算到結尾的 \n 或 \r。

Python 程式碼


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

first = True
for line in sys.stdin:
    if not first: print()
    first = False
    cnt = Counter(line.strip())
    ans = [(v, ord(k)) for k, v in cnt.items()]
    ans.sort(key = lambda x : (x[0], -x[1]))
    for a in ans: print(a[1], a[0])

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

result = []
lines = sys.stdin.readlines()
for line in lines:
    if result: result.append("\n")
    cnt = Counter(line.strip())
    ans = [(v, ord(k)) for k, v in cnt.items()]
    ans.sort(key = lambda x : (x[0], -x[1]))
    for a in ans: result.append(f"{a[1]:d} {a[0]:d}\n")
sys.stdout.write("".join(result))


2026年1月26日 星期一

ZeroJudge 解題筆記:c010. 10107 - What is the Median?

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


ZeroJudge 題目連結:c010. 10107 - What is the Median?

解題想法


這題如果用串列,每次插入一個新的數字之後再排序、取中位數,這樣一定會超時。我試過以下兩種解法。第一種是用 C++ 的 priority_queue 及 Python 的 heapq 儲存資料,小於等於中位數的值儲存在最大優先佇列 small 之中,大於中位數的值儲存在最小優先佇列 large 之中,並且保持 small 的數量與 large 相等或是多一個。當讀取到一筆新的資料 x 時有三種可能性:
  1. x 小於等於 small 最上面的資料:如果 small 的數量比 large 還多,將 small 最上面的資料移到 large。最後將 x 加入 small。
  2. large 有資料而且 x 小於 large 最上面的資料: 如果 small 與 large 數量相等,將 x 加入 small,反之將 x 加入 large。
  3. 其它狀況:如果 small 與 large 數量相等,將 large 最上面的資料移到 small。最後將 x 加入 large。
中位數有兩種可能性:
  1. 如果 small 數量比 large 還多,中位數就是 small 最上面的資料。
  2. 如果 small 與 large 數量相等,中位數是 small 與 large 最上面的資料平均值。

第二種解法是用 C++ algorithm 函式庫的 lower_bound 找出插入新資料並維持排序的位置,再用 vector.insert 插入新資料。在 Python 則可以用 bisect.insort 直接將新資料插入到 list 之中。由於這兩種工具是利用二分搜尋法找位置,速度相當快,寫起來也會比第一種法方簡單。

Python 程式碼


方法1,使用時間約為 21 ms,記憶體約為 4.2 MB,通過測試。
import sys, heapq

mid = int(sys.stdin.readline())
result = [f"{mid:d}\n"]
large, small = [], [-mid]
for line in sys.stdin:
    x = int(line)
    if x <= -small[0]:
        if len(small) > len(large):
            heapq.heappush(large, -heapq.heappop(small)) 
        heapq.heappush(small, -x)
    elif large and x < large[0]:
        if len(small) == len(large):
            heapq.heappush(small, -x) 
        else:
            heapq.heappush(large, x)
    else:
        if len(small) == len(large):
            heapq.heappush(small, -heapq.heappop(large))
        heapq.heappush(large, x)
    if len(small) > len(large):
        mid = -small[0]
    else:
        mid = (large[0] - small[0]) // 2
    result.append(f"{mid:d}\n")
sys.stdout.write("".join(result))


方法2,使用時間約為 26 ms,記憶體約為 4.8 MB,通過測試。
import sys
from bisect import insort

result = []
arr = []
lines = sys.stdin.readlines()
for line in lines:
    x = int(line)
    insort(arr, x)
    n = len(arr)
    if n%2 == 1:
        result.append(f"{arr[n//2]:d}\n")
    else:
        result.append(f"{(arr[n//2 - 1] + arr[n//2]) // 2:d}\n")
sys.stdout.write("".join(result))


2026年1月25日 星期日

ZeroJudge 解題筆記:c009. 10473 - Simple Base Conversion

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


ZeroJudge 題目連結:c009. 10473 - Simple Base Conversion

解題想法


這題如果用 Python 寫很簡單,可以用內建的工具 int(字串, 基底) 將字串以指定的基底轉換成十進位整數,也可以用 hex(整數) 將十進位整數轉換成 16 進位制字串。如果用 C++ 解題則需要自己寫轉換用的函式。

Python 程式碼


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

for s in sys.stdin:
    s = s.rstrip()
    if s[0] == "-": break
    elif s[:2] == "0x":
        print(int(s[2:], 16))
    else:
        t = hex(int(s))[2:].upper()
        print("0x" + t)

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

result = []
lines = sys.stdin.readlines()
for s in lines:
    s = s.strip()
    if not s: continue
    if s[0] == "-": break
    elif s[:2] == "0x":
        result.append(f"{int(s[2:], 16):d}\n")
    else:
        t = hex(int(s))[2:].upper()
        result.append(f"0x{t}\n")
sys.stdout.write("".join(result))


2026年1月24日 星期六

ZeroJudge 解題筆記:c007. 00272 - TeX Quotes

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


ZeroJudge 題目連結:c007. 00272 - TeX Quotes

解題想法


讀取一行測資,再依序讀取這行的每個字元並記錄讀到 " 的次數 left,讀到第 1 個 " 時將 left 設定成 1,在要輸出的字串中加上 ``;如果讀到 " 時 left 等於 1,代表這是第 2 個 ",將 left 歸零,在要輸出的字串中加上 '';如果是其它字串則直接加到要輸出的字串後面。

Python 程式碼


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

left = 0
result = []
lines = sys.stdin.readlines()
for line in lines:
    s = []
    for c in line.rstrip():
        if c == "\"":
            if left == 0:
                s += ["`", "`"]
                left = 1
            else:
                s += ["\'", "\'"]
                left = 0
        else: s.append(c)
    res = "".join(s)
    result.append(f"{res}\n")
sys.stdout.write("".join(result))


2026年1月23日 星期五

ZeroJudge 解題筆記:c006. 10550 - Combination Lock

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


ZeroJudge 題目連結:c006. 10550 - Combination Lock

解題想法


刻度差 1 要轉 9 度,順時鐘方向轉刻度減少,逆時鐘方向轉刻度增加。

Python 程式碼


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

for line in sys.stdin:
    if line.rstrip() == "0 0 0 0":
        break
    a, b, c, d = map(int, line.split())
    tot = 360*3
    tot += 360 - (9*b + 360 - 9*a) % 360
    tot += (9*c + 360 - 9*b) % 360
    tot += 360 - (9*d + 360 - 9*c) % 360
    print(tot)


2026年1月22日 星期四

ZeroJudge 解題筆記:c004. 10812 - Beat the Spread!

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


ZeroJudge 題目連結:c004. 10812 - Beat the Spread!

解題想法


這題在算數學。假設兩隊的分數分別為 $x, y$ 而且 $x \geq y$,因為分數為大於等於 0 的整數,因此 $s \geq d$。如果測資 $s < d$,直接印出 impossible。 接下來解聯立 $$ x + y = s ~~~~~ x - y = d $$ 將兩式相加可得 $$ 2x = s + d ~\Rightarrow~ x = \frac{s + d}{2} $$ 如果 $s + d$ 是奇數,直接印出 impossible。再將 $x$ 代入其中一條式子可得 $$ y = s - x $$

Python 程式碼


使用時間約為 9 ms,記憶體約為 2.8 MB,通過測試。
n = int(input())
for _ in range(n):
    s, d = map(int, input().split())
    if d > s:
        print("impossible")
        continue
    xx = s + d
    if xx%2 == 1:
        print("impossible")
        continue
    x = xx//2
    y = s - x
    print(x, y)


2026年1月21日 星期三

ZeroJudge 解題筆記:c001. 10405 - Longest Common Subsequence

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


ZeroJudge 題目連結:c001. 10405 - Longest Common Subsequence

解題想法


只要找最長共同子序列 (longest common subsequence, LCS) 的長度,用二維串列 dp 儲存兩個字串 s, t 的第 i 個、第 j 個字母對應的 LCS 長度。

Python 程式碼


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

def length_LCS(s, t):
    m, n = len(s), len(t)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(1, m+1):
        for j in range(1, n+1):
            if s[i-1] == t[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    return dp[m][n]

result = []
lines = sys.stdin.readlines()
idx = 0
while idx < len(lines):
    if not lines[idx].strip():
        idx += 1
        continue
    a = lines[idx].strip()
    idx += 1
    b = lines[idx].strip()
    idx += 1
    ans = length_LCS(a, b)
    result.append(f"{ans:d}\n")
sys.stdout.write("".join(result))

2026年1月20日 星期二

ZeroJudge 解題筆記:b917. 11059 Maximum Product

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


ZeroJudge 題目連結:b917. 11059 Maximum Product

解題想法


這題用兩層 for 迴圈硬算,假設要測試的數字存在串列 $nums$,依序測試從 $nums[i]$ 為首項,再乘以 $nums[j], (j < i)$ 到 $nums[-1]$,每次乘完之後更新最大值。時間複雜度約為 $O(n^2)$,可以通過測試。反而是輸出格式要小心,每一行測資對應的結果底下都要印出一行空行。

Python 程式碼


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

result = []
lines = sys.stdin.readlines()
idx = 0
ca = 0
while idx < len(lines):
    if not lines[idx].strip():
        idx += 1
        continue
    ca += 1
    n = int(lines[idx])
    idx += 1
    nums = list(map(int, lines[idx].split()))
    idx += 1
    imax = 0
    for i in range(n):
        curr = 1
        for j in range(i, n):
            curr *= nums[j]
            imax = max(imax, curr)
    result.append(f"Case #{ca:d}: The maximum product is {imax:d}.\n\n")
sys.stdout.write("".join(result))


2026年1月19日 星期一

ZeroJudge 解題筆記:b587. 10918 - Tri Tiling

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


ZeroJudge 題目連結:b587. 10918 - Tri Tiling

解題想法


因為地板的尺寸為 $3 \times n$,要放入的地板磚尺寸為 $1 \times 2$,如果 $n$ 為奇數時無解。接下來列出 $n$ 的前幾項可能的拼法,例如 $n = 2$ 有 3 種
AA   AB   CC
BB   AB   AB
CC   CC   AB
如果以 $dp[n]$ 代表地板尺寸為 $3 \times n$ 的拼法數量,其中 $dp[0] = 1$,因為沒有放置地板算 1 種;$dp[2] = 3$,像是上面的例子;接下來推算 $n \geq 4$ 的狀況。 狀況1,如果最右側是完整的 $3 \times 2$,這個部分有 3 種拼法,左側的拼法為 $dp[n-2]$,因此 $dp[n] = 3 \times dp[n-2]$。 狀況2,如果最右側不是完整的 $3 \times 2$,先列出 $$ dp[n] = 3 \times dp[n-2] + 2 \times (dp[n-4] + dp[n-6] + \dots + dp[0]) $$ 再列出 $$ dp[n-2] = 3 \times dp[n-4] + 2 \times (dp[n-6] + dp[n-8] + \dots + dp[0]) $$ 將兩式相減 $$ dp[n] - dp[n-2] = 3 \times dp[n-2] - dp[n-4] $$ 移項後得到 $$ dp[n] = 4 \times dp[n-2] - dp[n-4] $$

Python 程式碼


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

def solve(n):
    if n == 0: return 1
    if n == 2: return 3
    if n%2 == 1: return 0
    dp = [0]*(n+1)
    dp[0] = 1; dp[2] = 3
    for i in range(4, n+1, 2):
        dp[i] = 4*dp[i-2] - dp[i-4]
    return dp[n]

for line in sys.stdin:
    n = int(line)
    if n == -1: break
    print(solve(n))


2026年1月18日 星期日

ZeroJudge 解題筆記:b304. 00673 - Parentheses Balance

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


ZeroJudge 題目連結:b304. 00673 - Parentheses Balance

解題想法


先用字典儲存 3 種右括號對應的左括號,這樣在檢查括號是否成對時比較方便。先讀取一行測資,再依序讀取這行測資中的每個字元,如果是左括號就存入堆疊 st 之中,如果讀到右括號,檢查堆疊最上面是否為對應的左括號,如果成對就移除這筆資料,如果堆疊是空的或不是對應的左括號,無解,中止迴圈。最後再檢查堆疊是否還有資料,如果有,無解。

Python 程式碼


使用時間約為 18 ms,記憶體約為 3.3 MB,通過測試。
left = {')': '(', ']': '['}  # 左、右括號配對
n = int(input())  # n 組測資
for _ in range(n):  # 執行 n 次
    s = list(input())  # 字串轉成串列
    st = []  # 左括號堆疊
    flag = True  # 是否有解
    for c in s:  # 依序由 s 讀取字元 c
        if c in "([": st.append(c)  # 如果 c 是左括號放入堆疊
        else:  # 反之為右括號
            if not st or st[-1] != left[c]:  # 如果 st 是空的或是最後一項無法與 c 配對
                flag = False  # 無解
                break  # 中止迴圈
            else: st.pop() # 有解,移除 st 最後一項
    if st: flag = False  # 如果有剩下的左括號,無解
    print("Yes" if flag else "No")


2026年1月17日 星期六

ZeroJudge 解題筆記:a743. 10420 - List of Conquests

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


ZeroJudge 題目連結:a743. 10420 - List of Conquests

解題想法


這題用 map 及 set 儲存各國家的人名,讀取所有的測資之後,再將國家及人名數量組成 tuple 存入另一個 list 之中,將 list 依照人名數量由小到大排序,如果數量相同再依照國家名稱排序。

Python 程式碼


使用時間約為 23 ms,記憶體約為 4.2 MB,通過測試。
from collections import defaultdict

n = int(input())
cnt = defaultdict(set)
for _ in range(n):
    country, name = list(input().split(" ", 1))  # 用空格分格,只切一次
    cnt[country].add(name)
ans = sorted([(k, len(v)) for k, v in cnt.items()])
for a in ans: print(*a)


2026年1月16日 星期五

ZeroJudge 解題筆記:a741. 10101 - Bangla Numbers

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


ZeroJudge 題目連結:a741. 10101 - Bangla Numbers

解題想法


用自訂函式 convert,將輸入的整數 n 轉換成題目要的字串。先處理 $n = 0$ 的特例,直接回傳字串 0。如果 $n > 1000000000$,需要先處理這部分的係數,也就是 kuti 的數量。接下來再依序處理 lakh, hajar, shata 的數量。

Python 程式碼


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

def convert(n):  # 輸入數字 n,輸出答案字串
    if n == 0: return "0"  # 特例,直接回傳 0
    unit = (10000000, 100000, 1000, 100)
    word = ("kuti", "lakh", "hajar", "shata")
    result = []  # 儲存答案用的串列,內容為 [數量, 單位, ...]
    if n > 1000000000:  # 前面的位數可以再細分
        left = n // 10000000  # 前面的位數,上限為 99999999
        n %= 10000000  # 剩下的位數
        for u, w in zip(unit, word):
            r = left // u  # 取這個單位對應的數量
            if r > 0: result += [str(r), w]  # r 大於 0,r 轉成字串,r, w 加到 result
            left %= u  # left 取餘數
        if left > 0: result.append(str(left))  # 剩下的位數
        result.append("kuti")  # 前面整串代表 kuti 的數量
    for u, w in zip(unit, word):  # 由大到小讀取單位對應的數值、字串
        r = n // u  # 取這個單位對應的數量
        if r > 0: result += [str(r), w]  # r 大於 0,r 轉成字串,r, w 加到 result
        n %= u  # n 取餘數
    if n > 0: result.append(str(n))  # 如果有小於 100 的值,加到 result
    return " ".join(result)  # 用空格接成字串再回傳

result = []
lines = sys.stdin.readlines()  # 讀取所有測資
idx = 0
while idx < len(lines):
    n = int(lines[idx])
    idx += 1
    result.append(f"{idx:3d}. {convert(n):s}\n")
sys.stdout.write("".join(result))


2026年1月15日 星期四

ZeroJudge 解題筆記:a676. 00111 - History Grading

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


ZeroJudge 題目連結:a676. 00111 - History Grading

解題想法


寫法1,找正確的事件編號排序與學生作答的編號排序最長共同子序列長度 (longest common subsecquence, LCS)。寫法2,找最長遞增子序列 (longest increasing subsecquence, LCS)。

Python 程式碼


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

def length_LCS(a, b):  # 輸入串列 a, b,回傳 LCS 長度
    m, n = len(a), len(b)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(1, m+1):
        for j in range(1, n+1):
            if a[i-1] == b[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    return dp[m][n]

n = int(input())  # n 個事件
ans = [0]*n  # 事件編號正確的排序
for i, a in enumerate(map(int, input().split()), start=1): ans[a-1] = i
for line in sys.stdin:  # 讀取學生的答案
    stu = [0]*n  # 學生回答的事件編號排序
    for i, a in enumerate(map(int, line.split()), start=1): stu[a-1] = i
    print(length_LCS(ans, stu))  # 找 ans, stu 的 LCS 長度

使用時間約為 0.2 s,記憶體約為 4.7 MB,通過測試。
import sys
from bisect import bisect_left

def length_LIS(nums):  # 找最長遞增子序列長度
    tails = []
    for num in nums:
        idx = bisect_left(tails, num)
        if idx == len(tails): tails.append(num)
        else: tails[idx] = num
    return len(tails)

n = int(input())  # n 個事件
ans = dict()  # 事件編號: 正確排序的索引值
for i, a in enumerate(map(int, input().split()), start=1): ans[i] = a-1
for line in sys.stdin:  # 讀取學生的答案
    stu = [0]*n  # 學生回答的事件編號換成正確答案對應的索引值,如果全對為 0, 1, ..., n-1
    for i, a in enumerate(map(int, line.split()), start=1): stu[a-1] = ans[i]
    print(length_LIS(stu))


2026年1月14日 星期三

ZeroJudge 解題筆記:a674. 10048 - Audiophobia

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


ZeroJudge 題目連結:a674. 10048 - Audiophobia

解題想法


這題考 Floyd-Warshall 演算法。

Python 程式碼


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

result = []
lines = sys.stdin.readlines()
idx = 0
ca = 0  # case number
while idx < len(lines):
    if not lines[idx].strip():
        idx += 1
        continue
    if lines[idx].strip() == "0 0 0":
        break
    C, S, Q = map(int, lines[idx].split())
    idx += 1
    ca += 1
    if result:  # 如果 result 已經有內容,先換行
        result.append("\n")
    result.append(f"Case #{ca:d}\n")
    # 建立儲存節點 u 到 v 噪音值的接鄰矩陣,預設為無窮大,代表不連通
    cost = [[float('inf')]*(C+1) for _ in range(C+1)]
    for i in range(C+1):  # 同一個節點成本為 0
        cost[i][i] = 0
    for _ in range(S):  # 節點 u 到 v 成本
        u, v, d = map(int, lines[idx].split())
        idx += 1
        cost[u][v] = d
        cost[v][u] = d
    # Floyd-Warshall 演算法,以 k 為中繼點,從 i 到 j 的最大噪音值
    for k in range(1, C+1):
        for i in range(1, C+1):
            for j in range(1, C+1):
                if cost[i][k] != float('inf') and cost[k][j] != float('inf'):
                    if cost[i][j] > max(cost[i][k], cost[k][j]):
                        cost[i][j] = max(cost[i][k], cost[k][j])
    # Q 次查詢
    for _ in range(Q):
        u, v = map(int, lines[idx].split())
        idx += 1
        if cost[u][v] == float('inf'):
            result.append("no path\n")
        else:
            result.append(f"{cost[u][v]:d}\n")
sys.stdout.write("".join(result))


2026年1月13日 星期二

ZeroJudge 解題筆記:a673. 10026 - Shoemaker's Problem

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


ZeroJudge 題目連結:a673. 10026 - Shoemaker's Problem

解題想法


先依照每日平均罰款金額由大到小排序,如果相同再依照讀取順序排序。

Python 程式碼


由於這題的測資有許多的空行,用 Python 解題時需要適時地跳過這些空行,導致以下的程式碼有些複雜。使用時間約為 8 ms,記憶體約為 3.3 MB,通過測試。
import sys

lines = sys.stdin.read().split('\n')
M = len(lines)
head = 0
T = int(lines[head])
head += 1
first_case = True

while T > 0:
    while head < M and lines[head].strip() == "": head += 1
    if head >= M: break
    N = int(lines[head])
    head += 1
    jobs = []
    for i in range(1, N+1):
        day, fine = map(int, lines[head].split())
        head += 1
        jobs.append((day, fine, i))
    jobs.sort(key = lambda x : (-(x[1]/x[0]), x[2]))
    if not first_case: print()
    print(" ".join([str(i) for _, _, i in jobs]))
    first_case = False
    T -= 1

改用 sys.stdin.read().split() 及 iter 可以讓程式碼比較簡潔,但是速度上反而慢了一點。使用時間約為 14 ms,記憶體約為 3.3 MB,通過測試。
import sys

result = []
lines = iter(sys.stdin.read().split())
T = int(next(lines))

for _ in range(T):
    N = int(next(lines))
    jobs = []
    for i in range(1, N+1):
        day = int(next(lines))
        fine = int(next(lines))
        jobs.append((day, fine, i))
    jobs.sort(key = lambda x : (-(x[1]/x[0]), x[2]))
    if result:  # 如果 result 已經有內容,先換行
        result.append("\n")
    res = " ".join([str(i) for _, _, i in jobs])
    result.append(f"{res}\n")
sys.stdout.write("".join(result))


2026年1月12日 星期一

ZeroJudge 解題筆記:a672. 00155 - All Squares

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


ZeroJudge 題目連結:a672. 00155 - All Squares

解題想法


因為以大小為 $k$ 的正方形頂點向外擴張最大範圍為 $$ k_{max} = k + (k-2) + (k-4) + \dots + 1 = \frac{(k+1)^2}{4} $$ 如果 $(x, y)$ 在四個頂點 $\pm k_{max}$ 以外的範圍,可以不需要再加到堆疊之中,可以少算很多次。

Python 程式碼


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

def count_squares(k, x, y):  # 輸入大小 k,要找的點座標 (x, y)
    cnt = 0  # (x, y) 被幾個正方形包圍
    st = [(k, 1024, 1024)]  # 要計算的正方形大小、中心位置
    while st:  # 如果 st 還有資料繼續執行
        kc, xc, yc = st.pop()  # 目前的正方形大小、中心位置
        left, right = xc-kc, xc+kc  # 左、右頂點位置
        top, bottom = yc-kc, yc+kc  # 上、下頂點位置
        if left <= x <= right and top <= y <= bottom:  # (x, y) 在這個正方形之中
            cnt += 1  # cnt 加 1
        kn = kc//2  # 下一個正方形的大小
        if kn >= 1:  # 下一個正方形的中心
            kmax = (kc+1)*(kc+1)//4  # 以這個正方形頂點向外擴張的上限,公差為 2 的等差級數
            if left-kmax <= x <= left+kmax and top-kmax <= y <= top+kmax: st.append((kn, left, top))
            if left-kmax <= x <= left+kmax and bottom-kmax <= y <= bottom+kmax: st.append((kn, left, bottom))
            if right-kmax <= x <= right+kmax and top-kmax <= y <= top+kmax: st.append((kn, right, top))
            if right-kmax <= x <= right+kmax and bottom-kmax <= y <= bottom+kmax:st.append((kn, right, bottom))
    return cnt  # 回傳數量

for line in sys.stdin:
    k, x, y = map(int, line.split())
    if k == 0 and x == 0 and y == 0: continue
    print(f"{count_squares(k, x, y):3d}")


2026年1月11日 星期日

ZeroJudge 解題筆記:a540. 10684 - The jackpot

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


ZeroJudge 題目連結:a540. 10684 - The jackpot

解題想法


這題考 Kadane's Algorithm,詳細的說明可以參考這篇 Maximum Subarray Sum - Kadane's Algorithm

Python 程式碼


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

result = []
lines = sys.stdin.readlines()
idx = 0
while idx < len(lines):
    n = int(lines[idx])
    idx += 1
    if n == 0: break
    nums = list(map(int, lines[idx].split()))
    idx += 1
    curr = imax = nums[0]
    for num in nums[1:]:
        # 如果用前面累積的值加上 num 較大,取這個值,反之取 num 重新開始
        curr = max(num, curr + num)
        imax = max(imax, curr)
    if imax > 0:
        result.append(f"The maximum winning streak is {imax:d}.\n")
    else:
        result.append("Losing streak.\n")
sys.stdout.write("".join(result))


2026年1月10日 星期六

ZeroJudge 解題筆記:a539. 10327 - Flip Sort

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


ZeroJudge 題目連結:a539. 10327 - Flip Sort

解題想法


看起來像是考氣泡排序,但是不能直接跑氣泡排序並計算交換次數,這樣會很慢。從最後一個數字往前找,計算這個數字之前有幾個比較大的數字,將數量相加就是答案。

Python 程式碼


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

result = []
lines = sys.stdin.readlines()
idx = 0
while idx < len(lines):
    n = int(lines[idx])
    idx += 1
    arr = list(map(int, lines[idx].split()))
    idx += 1
    cnt = 0
    for i in range(n-1, 0, -1):
        for j in range(i-1, -1, -1):
            if arr[i] < arr[j]:
                cnt += 1
    result.append(f"Minimum exchange operations : {cnt:d}\n")
sys.stdout.write("".join(result))


2026年1月9日 星期五

ZeroJudge 解題筆記:a537. 10789 - Prime Frequency

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


ZeroJudge 題目連結:a537. 10789 - Prime Frequency

解題想法


先用埃拉托斯特尼篩法建立 0 到 2000 之間的質數篩選器,再對每一行字串用字典或表格計數,計算字母數量,如果數量是質數就加到答案之中。最後再依照答案中儲存的內容按照字典序輸出,如果答案是空的則印出 empty。

Python 程式碼


Python 有現成的計數工具 Counter,使用時間約為 11 ms,記憶體約為 3.2 MB,通過測試。
from collections import Counter
""" 建立 0 ~ 2000 的質數篩選器 """
maxn = 2000
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
""" 主要的解題過程 """
T = int(input())
for t in range(1, T+1):
    cnt = Counter(input())  # 計數器
    ans = []  # 答案
    for k, v in cnt.items():  # 依序取出字母 k 及數量 v
        if sieve[v]: ans.append(k)  # 如果 v 是質數,k 加入 ans
    ans.sort()  # 排序
    print(f"Case {t:d}: ", end="")  # 輸出答案
    if not ans: print("empty")
    else: print("".join(ans))


2026年1月8日 星期四

ZeroJudge 解題筆記:a535. 10141 - Request for Proposal

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


ZeroJudge 題目連結:a535. 10141 - Request for Proposal

解題想法


依序掃過所有的廠商資料,如果讀到新的最大商品數量、最低價格,更新贏家名稱。

Python 程式碼


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

ca = 0
for line in sys.stdin:
    n, p = map(int, line.split())  # 需求表項目數量 n、廠商數量 p
    ca += 1
    if n == 0 and p == 0: break
    for _ in range(n):  # 讀取 n 行資料,解題時用不到
        _ = sys.stdin.readline()
    winner = ""  # 贏家
    price = float('inf')  # 最低價格,預設為最大值
    item = 0  # 廠商提供的商品在需求表中的最大數量
    for _ in range(p):  # 讀取 p 家廠商資料
        name = sys.stdin.readline().rstrip()  # 廠商名稱
        d, r = sys.stdin.readline().split()  # 價格 d、商品數量 r
        d = float(d)  # d 轉成浮點數
        r = int(r)  # r 轉成整數
        if r > item:  # 如果 r 大於 item
            winner = name  # 新的贏家
            price = d  # 新的最低價
            item = r  # 新的商品數量
        elif r == item and d < price:  # 如果商品數量一樣而且價格較低
            winner = name  # 新的贏家
            price = d  # 新的最低價
        for _ in range(r):  # 讀取 r 行資料,解題時用不到
            _ = sys.stdin.readline()
    if ca > 1: sys.stdout.write("\n")
    sys.stdout.write(f"RFP #{ca:d}\n{winner:s}\n")  


2026年1月7日 星期三

ZeroJudge 解題筆記:a522. 12455 - Bars

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


ZeroJudge 題目連結:a522. 12455 - Bars

解題想法


這題考 0/1 背包問題,由於測資不大,用 set 儲存所有可能的長度組合也能 AC,比較標準的作法是用一維陣列儲存所有長度總合的組合數,或是用 bitset 儲存所有可能的長度組合。

Python 程式碼


用集合儲存所有可能的長度組合,使用時間約為 22 ms,記憶體約為 3.3 MB,通過測試。
t = int(input())  # t 組測資
for _ in range(t):  # 執行 t 次
    n = int(input())  # 需要的長度 n
    have = {0}  # 已經有的長度集合,先放入 0
    p = int(input())  # p 根金屬棒,用不到
    for m in map(int, input().split()):  # 讀取金屬棒長度
        tmp = set()  # 暫存新的總長度
        for h in have: tmp.add(h+m)  # 依序讀取已經有的長度 h,新的長度 h+m 加入 tmp
        #for t in tmp: have.add(t)  # tmp 的資料加入 have
        have.update(tmp)  # tmp 的資料加入 have,另一種寫法
    print("YES" if n in have else "NO")  # 如果 n 在 have 之中印出 YES,反之印出 NO

用動態規劃找出所有長度可能的組合數,如果長度 $n \leq imax$ 而且組合數大於 0 印出 Yes,反之印出 No。使用時間約為 10 ms,記憶體約為 2.9 MB,通過測試。
t = int(input())  # t 組測資
for _ in range(t):  # 執行 t 次
    n = int(input())  # 需要的長度 n
    p = int(input())  # p 根金屬棒,用不到
    ms = list(map(int, input().split()))  # 金屬棒長度
    imax = sum(ms)  # 總長度
    dp = [0]*(imax + 1)  # 所有長度可能的組合數
    dp[0] = 1  # 基礎狀態,長度 0 的組合數 1
    for m in ms:  # 依序讀取金屬棒長度,0/1 背包問題
        for i in range(imax, m-1, -1):
            if dp[i-m] > 0:
                dp[i] += dp[i-m]
    # 如果長度 n 小於等於 imax 而且組合數大於 0 印出 Yes
    print("YES" if n <= imax and dp[n] > 0 else "NO")  

用動態規劃及 bitset 儲存所有可能的長度組合,不記錄組合數,如果長度 $n$ 有對應的組合印出 Yes,反之印出 No。使用時間約為 7 ms,記憶體約為 2.8 MB,通過測試。
t = int(input())  # t 組測資
for _ in range(t):  # 執行 t 次
    n = int(input())  # 需要的長度 n
    p = int(input())  # p 根金屬棒,用不到
    ms = list(map(int, input().split()))  # 金屬棒長度
    dp = 1  # 用 bitset 儲存所有可能的長度組合,基礎狀態,長度 0 的組合數 1
    for m in ms:  # 依序讀取金屬棒長度,0/1 背包問題
        dp |= (dp << m)
    # 如果長度 n 小於等於 imax 而且組合數大於 0 印出 Yes
    print("YES" if (dp >> n) & 1 else "NO")  


2026年1月6日 星期二

ZeroJudge 解題筆記:a520. 12416 - Excessive Space Remover

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


ZeroJudge 題目連結:a520. 12416 - Excessive Space Remover

解題想法


這題要先排除特例,如果字串中所有的連續空格都是 1 格,不需要取代空格,印出 0 即可。 接下來用數學解。先計算連續空格的數量最大值 $imax$。 狀況1,如果 $imax = 2^n$,每取代一次會使 $imax$ 減半,共要取代 $n$ 次。例如 $imax = 16$,每次取代空格之後,$imax$ 分別變為 $8, 4, 2, 1$,共 4 次。 狀況2,如果 $2^{n-1} < imax < 2^n$,需要取代 $n$ 次。例如 $imax = 13$,每次取代空格之後,$imax$ 分別變為 $7, 4, 2, 1$,共 4 次。

Python 程式碼


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

for line in sys.stdin:
    s = line.rstrip()
    imax, cnt = 0, 0
    for c in s:
        if c.isspace():
            cnt += 1
        else:
            imax = max(imax, cnt)
            cnt = 0
    imax = max(imax, cnt)
    if imax == 1:
        print(0)
    else:
        print(math.ceil(math.log2(imax)))


2026年1月5日 星期一

ZeroJudge 解題筆記:a519. 12459 - Bees' ancestors

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


ZeroJudge 題目連結:a519. 12459 - Bees' ancestors

解題想法


先列出幾代的祖先看一下數量是否有規律,以下各行依序為第幾代,祖先性別 M 為雄性、F 為雌性,祖先數量。
1, F, 1
2, FM, 2
3, FMF, 3
4, FMFFM, 5
5, FMFFMFMF, 8
6, FMFFMFMFFMFFM, 13
第 $n$ 代的祖先數量就是費氏數列 $F(n+1)$ 對應的值。由於這題有多筆測資要查詢對應的數量,可以先建表格,節省查詢的時間。

Python 程式碼


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

fib = [0]*82
fib[1] = 1
for i in range(2, 82):
    fib[i] = fib[i-1] + fib[i-2]

for line in sys.stdin:
    n = int(line)
    if n == 0: break
    print(fib[n+1])


2026年1月4日 星期日

ZeroJudge 解題筆記:a518. 12468 - Zapping

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


ZeroJudge 題目連結:a518. 12468 - Zapping

解題想法


有三種可能性,如果 a 等於 b,不需要切換,答案為 0;如果 a 大於 b,按下再從另一頭繞回來,或是一直按上;如果 a 小於 b,按上再從另一頭繞回來,或是一直按下。對後面兩種狀況,分別計算按上或下的次數,答案是較小的那個。

Python 程式碼


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

for line in sys.stdin:
    a, b = map(int, line.split())
    if a == -1 and b == -1: break
    up, down = 100, 100  # 按上、下最少的次數,預設為超出範圍的值
    if a == b:  # a 等於 b,不需切換
        up = down = 0
    elif a > b:  # a 大於 b,按下再從另一頭繞回來,或是一直按上
        up = a - b
        down = 100 - a + b
    else:  # a 小於 b,按上再從另一頭繞回來,或是一直按下
        up = a + 100 - b
        down = b - a
    print(min(up, down))


2026年1月3日 星期六

ZeroJudge 解題筆記:a469. 10063 - Knuth's Permutation

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


ZeroJudge 題目連結:a469. 10063 - Knuth's Permutation

解題想法


題目的文字敘述不太容易理解,討論區這篇貼文的解釋比較詳細 https://zerojudge.tw/ShowThread?postid=18884&reply=18880#18884。寫一個自訂函式 solve,依照 Knuth's Permutation 的規則産生所有的排列方式。

Python 程式碼


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

def solve(letters):  # 輸入字串,依照 Knuth's Permutation 的規則産生所有的排列方式
    perms = [letters[0]]  # 所有的排列方式,先放入 letters[0]
    for letter in letters[1:]:  # 依序讀取 letters[1] ~ letters[-1]
        new_perms = []  # 新的排列方式
        for perm in perms:  # 依序讀取 perms 每一項
            for i in range(len(perm) + 1):  # 依序找 i = 0 ~ len(perm) 的位置
                new_perm = perm[:i] + letter + perm[i:]  # 用字串切片,從左到右的位置組合新的排列方式
                new_perms.append(new_perm)  # new_perm 加入 new_perms
        perms = new_perms  # 更新 perms,每跑完一次 perms 中每一項長度加 1
    return perms  # 回傳 perms

for line in sys.stdin:
    perms = solve(line.strip())
    for perm in perms: print(perm)
    print()


2026年1月2日 星期五

ZeroJudge 解題筆記:a468. 12439 - February 29

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


ZeroJudge 題目連結:a468. 12439 - February 29

解題想法


先自訂 3 個函式,分別為用來檢查指定年份是否為閏年的 is_leap,計算指定年份到西元 0 年的閏年數量的 leaps_before,計算兩個年份之間的閏年數量的 count_leap_years。讀取日期之後,將日期的年、月、日轉成整數。如果起始年在2月29日之後,則不計入該年。如果結束年在2月29日之前,則不計入該年。將調整後的起始年、結束年代入 count_leap_years 計算答案。

Python 程式碼


使用時間約為 24 ms,記憶體約為 3.4 MB,通過測試。
def is_leap(year):  # 判斷輸入的年份是否為閏年
    if year % 400 == 0: return True  # 可以被 400 整除,是閏年
    if year % 100 == 0: return False  # 可以被 100 整除但不能被 400 整除,是平年
    if year % 4 == 0: return True  # 可以被 4 整除但不能被 100 整除,是閏年
    return False  # 其它狀況,是平年

def leaps_before(year):  # 計算指定年份到西元 0 年的閏年數量
    return year//4 - year//100 + year//400

def count_leap_years(start_year, end_year):  # 計算兩個年份之間的閏年數量,包含兩端的年份
    return leaps_before(end_year) - leaps_before(start_year - 1)

""" 主要的解題過程 """
month_map = {"January": 1, "February": 2, "March": 3, "April": 4, "May": 5, "June": 6,
             "July": 7, "August": 8, "September": 9, "October": 10, "November": 11, "December": 12}
T = int(input())  # T 筆測資
for t in range(1, T+1):  # 執行 T 次
    ### 處理起始日期 ###
    date1 = list(input().split())  # 起始日期
    month1 = month_map[date1[0]]  # 起始月份
    day1 = int(date1[1][:-1])  # 起始日,刪除最後的逗號
    year1 = int(date1[2])  # 起始年份
    ### 處理結束日期 ###
    date2 = list(input().split())  # 結束日期
    month2 = month_map[date2[0]]  # 結束月份
    day2 = int(date2[1][:-1])  # 結束日,刪除最後的逗號
    year2 = int(date2[2])  # 結束年份
    ### 調整起始年份和結束年份 ###
    start_year = year1
    end_year = year2
    if is_leap(year1):  # 如果起始年在2月29日之後,則不計入該年
        if month1 > 2 or (month1 == 2 and day1 > 29):
            start_year += 1
    if is_leap(year2):  # 如果結束年在2月29日之前,則不計入該年
        if month2 < 2 or (month2 == 2 and day2 < 29):
            end_year -= 1
    ### 計算閏年數量 ###
    if start_year > end_year:  # 題目保證結束日期在起始日期之後,基本上不會發生
        ans = 0
    else:
        ans = count_leap_years(start_year, end_year)
    print(f"Case {t:d}: {ans:d}")


2026年1月1日 星期四

ZeroJudge 解題筆記:a467. 11398 - The Base-1 Number System

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


ZeroJudge 題目連結:a467. 11398 - The Base-1 Number System

解題想法


依照題目敘述處理讀取到的字串,如果字串內容只有 ~ 就中止程式; 如果是其它內容,將字串用空格分隔後存入串列 arr 之中。可以用字串或是串列儲存一行測資轉換後得到的 0, 1 字串 result。依序讀取 arr 的內容存入 a,如果 a 是 # 將 result 用 2 進位制轉換成整數後輸出;如果 a 是 0,將 flag 改成 1; 如果 a 是 1,將 flag 改成 0;其它狀況,在 result 後面接上 a 的長度減 2 個 flag。

Python 程式碼


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

result = ""
flag = "1"
for line in sys.stdin:
    if line.strip() == "~": break
    arr = line.split()
    for a in arr:
        if a == "#":
            print(int(result, 2))
            result = ""
        elif a == "0": flag = "1"
        elif a == "00": flag = "0"
        else:
            result += flag*(len(a)-2)

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

result = []
lines = sys.stdin.readlines()
flag = "1"
idx = 0
while idx < len(lines):
    if lines[idx].strip() == "~": break
    arr = lines[idx].split()
    idx += 1
    res = []  # 暫存這行測資輸出結果用的串列
    for a in arr:
        if a == "#":
            num = int("".join(res), 2)
            res.clear()
            result.append(f"{num:d}\n")
        elif a == "0": flag = "1"
        elif a == "00": flag = "0"
        else:
            res += [flag]*(len(a)-2)
sys.stdout.write("".join(result))