置頂

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

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

熱門文章

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