置頂

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

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

熱門文章

2025年5月19日 星期一

ZeroJudge 解題筆記:l918. P1. 彈珠汽水 (Soda)

作者:王一哲
日期:2025年5月19日



ZeroJudge 題目連結:l918. P1. 彈珠汽水 (Soda)

解題想法


這題有一個陷阱,如果 $N = 1$,每喝一瓶汽水就可以再換一瓶新的汽水,如果寫 while 迴圈的條件時只考慮是否還有沒喝的汽水,這樣會變成無窮迴圈。

Python 程式碼


使用時間約為 38 ms,記憶體約為 3.3 MB,通過測試。
m, n, k = map(int, input().split())  # m 瓶彈珠汽水,n 個瓶子再換一瓶汽水,想要喝到 k 瓶
t, b = 0, 0  # 總瓶數,空瓶數
while m > 0 and t < k:  # 如果還有沒喝的汽水而且還沒有喝到想要的瓶數
    t += m; b += m  # 更新 t, b,加上 m
    m = b//n  # 用空瓶換汽水
    b %= n  # 剩下的空瓶
print("YES" if t >= k else "NO")  # 依照 t 是否大於等於 k 印出答案


2025年5月18日 星期日

ZeroJudge 解題筆記:k931. P8. 切割蜂蜜 (Honey)

作者:王一哲
日期:2025年5月18日



ZeroJudge 題目連結:k931. P8. 切割蜂蜜 (Honey)

解題想法


這題有 3 個重點:
  1. 要設計儲存六角形蜂巢資料的方式,我是用二維串列,偶數列的欄位為 1, 3, 5, ...,奇數列的欄位為 0, 2, 4, ...,這個寫法的優點是讀取資料時索引值比較好寫,缺點是會浪費接近一半的二維串列空間。
  2. 用動態規畫計算從左到右切割到每一格時最小的累積損失量,取這一格的左上、左側、左下之中的最小值,再加上這格原來的值。
  3. 為了避免檢查邊界值要寫一堆 if,我選擇在四周 (C++) 或是最右側、最下方 (Python) 加上正無窮大的值當作哨兵,缺點是要多花一點記憶體。
經過測試,這個寫法在目前通過測試的人當中,C++ 及 Python 使用的時間都最短,可能其他人都很認真地檢查是否出界吧!

Python 程式碼


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

oo = float('inf')
for line in sys.stdin:
    H, W = map(int, line.split())  # 晶格的高與寬
    grid = [[-1]*(2*W+2) for _ in range(H)]  # 晶格 (H+1)*(2*W+2)
    grid.append([oo]*(2*W+2))  # 最下方補一列 oo
    for r in range(H):  # 讀取 H 行資料
        row = list(map(int, input().split()))
        if r%2 == 0:  # 如果是偶數列,欄位為 1, 3, 5, ..., 2*W - 1
            for i, v in enumerate(row):  # 索引值 i,數值 v
                grid[r][2*i+1] = v
            grid[r][2*W] = grid[r][2*W+1] = oo  # 最後面補上 oo 當作哨兵
        else:  # 如果是奇數列,欄位為 0, 2, 4, ..., 2*W
            for i, v in enumerate(row):  # 索引值 i,數值 v
                grid[r][2*i] = v
            grid[r][2*W+1] = oo  # 最後面補上 oo 當作哨兵
    # 動態規畫,grid[r][c] 改成到這格損失的值,加上左上、左側、左下最小值
    for c in range(2, 2*W+1):  # 依序掃過第 2 ~ 2*W 欄
        if c%2 == 0:  # 如果是偶數欄,列為 1, 3, 5, ...
            for r in range(1, H, 2):
                grid[r][c] += min(grid[r-1][c-1], grid[r][c-2], grid[r+1][c-1])
        else:  # 如果是奇數欄,列為 0, 2, 4, ...
            for r in range(0, H, 2):
                grid[r][c] += min(grid[r-1][c-1], grid[r][c-2], grid[r+1][c-1])
    # 找出答案,在每一列的最後一格,不含 oo
    ans = oo  # 答案預設為最大值
    for r in range(H):  # 依序掃過每一列
        if r%2 == 0: ans = min(ans, grid[r][2*W-1])  # 如果是偶數列
        else: ans = min(ans, grid[r][2*W])   # 如果是奇數列
    print(ans)


2025年5月17日 星期六

ZeroJudge 解題筆記:k930. P7. 興建圍籬 (Fence)

作者:王一哲
日期:2025年5月17日



ZeroJudge 題目連結:k930. P7. 興建圍籬 (Fence)

解題想法


這題我用 BFS,但是在 Python 不論是用 deque 或 list 都太慢,在 C++ 用 queue 或 vector 都要跑 0.1 s。

Python 程式碼


寫法1,用 deque 儲存待走訪的佇列,但是速度不夠快,只能通過 60% 的測資。
import sys
from collections import deque

for line in sys.stdin:
    H, W = map(int, line.split())  # 地圖 H*W
    grid = [list(map(int, input().split())) for _ in range(H)]  # 讀取地圖
    num = 1  # 花地編號
    ans = []  # 答案
    for i in range(H):  # 依序掃過每一格
        for j in range(W):
            if grid[i][j] == 1:  # 如果此格是花地
                tot = 0  # 這區的圍籬數量
                que = deque([(i, j)])  # 待走訪的佇列
                num += 1  # 編號加 1
                while que:  # 如果 que 還有資料繼續執行
                    r, c = que.popleft()  # 從 que 開頭讀取並移除資料
                    grid[r][c] = num  # 設定 grid[r][c] 的編號
                    for dr, dc in ((0, 1), (1, 0), (0, -1), (-1, 0)):  # 四方位檢查
                        nr, nc = r+dr, c+dc  # 要檢查的格子 (nr, nc)
                        if 0 <= nr < H and 0 <= nc < W:  # 如果 nr, nc 沒有出界
                            if grid[nr][nc] == 1:  # 如果是還沒有編號的花地
                                grid[nr][nc] = num  # 設定編號
                                que.append((nr, nc))  # (nr, nc) 加入 que
                            elif grid[nr][nc] == 0:  # 如果是空地
                                tot += 1  # (r, c) 需要加一片圍籬
                ans.append(tot)  # 將 tot 加入 ans
    print(*sorted(ans))  # 將 ans 排序後印出

2025年5月16日 星期五

ZeroJudge 解題筆記:k926. P3. 錯字校正 (Correction)

作者:王一哲
日期:2025年5月16日



ZeroJudge 題目連結:k926. P3. 錯字校正 (Correction)

解題想法


這題測資不大,按照題目的要求模擬替換字母的過程,最後再計算原來的字串與替換後的字串不同的字母數量即可。

Python 程式碼


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

for line in sys.stdin:
    s = list(line.strip())  # 輸入的字串
    t = list(input().strip())  # 正確的字串
    n = int(input())  # 修正次數
    for _ in range(n):  # 讀取 n 行修正資料
        x, y = list(input().split())  # x 替換成 y
        for i in range(len(s)):  # 依序由 s 讀取字元
            if s[i] == x: s[i] = y  # 如果 s[i] 是 x 則換成 y
    diff = sum([a != b for a, b in zip(s, t)])  # 計算 s, t 之中對應位置不相同的字母數量
    print(diff)


2025年5月15日 星期四

ZeroJudge 解題筆記:k925. P2. 海底探險 (Adventure)

作者:王一哲
日期:2025年5月15日



ZeroJudge 題目連結:k925. P2. 海底探險 (Adventure)

解題想法


這題我沒有想到比較簡潔的寫法,只能按照題目的敘述模擬烏龜的運動過程,程式碼寫起來很長。

Python 程式碼


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

def solve(L, T, hs):
    rem = T-1  # 剩下的探索時間,先減 1
    hmax = max(hs)  # 最高區塊高度
    imax = hs.index(hmax)  # 最高區塊位置
    t1 = hmax - hs[0]  # 上升到最高區塊高度需要的時間
    rem -= t1  # 更新 rem
    if rem <= 0: return 1  # 已經用完探索時間,還在起點區塊,回傳 1
    t2 = imax  # 向右游到最高區塊需要的時間
    rem -= t2  # 更新 rem
    if rem <= 0: return T-t1  # 向右游到的區塊 T-1-t1+1
    if imax == L-1:  # 如果游到最右邊
        hmin = min(hs[1:L-1])  # hs[1] ~ hs[L-2] 最低點
        imin = L-2 - hs[1:L-1][::-1].index(hmin)  # hs[1] ~ hs[L-2] 最低點位置
        t3 = L-1-imin  # 向左游到 imin 需要的時間
        rem -= t3  # 更新 rem
        if rem <= 0: return L-(rem+t3)  # 游到區塊 2 前時間就到了
        return imin + 1  # 可以游到區塊 imin + 1
    else:  # 不是游到最右邊
        hmax2 = max(hs[imax+1:])  # imax 右側最高的區塊高度
        imax2 = hs[imax+1:].index(hmax2) + imax + 1  # hmax2 的位置
        t3 = imax2-imax  # 向右游到 imin2 需要的時間
        rem -= t3  # 更新 rem
        if rem <= 0: return imax+rem+t3+1  # 游到 imax2 + 1 前時間就到了
        return imax2 + 1  # 可以游到 imax2 + 1

for line in sys.stdin:
    L = int(line)  # L 個區塊
    hs = list(map(int, input().split()))  # 各區塊高度
    T = int(input())  # 探索時間
    print(solve(L, T, hs))


2025年5月14日 星期三

ZeroJudge 解題筆記:k924. P1. 數字結合 (Combination)

作者:王一哲
日期:2025年5月14日



ZeroJudge 題目連結:k924. P1. 數字結合 (Combination)

解題想法


先將兩個字串合併,再計算奇數位數和、偶數位數和,如果兩者的差是 11 的倍數印出 Yes,反之印出 No。

Python 程式碼


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

for line in sys.stdin:
    s = "".join(line.split())  # 兩個整數合併
    odd, even = 0, 0  # 奇數位數和,偶數位數和
    for i, c in enumerate(s):  # 計算奇數位數和、偶數位數和
        if i%2 == 1: odd += int(c)
        else: even += int(c)
    dif = abs(odd - even)
    print("Yes" if dif%11 == 0 else "No")


2025年5月13日 星期二

ZeroJudge 解題筆記:k866. 8.運送隕石 (Delivery)

作者:王一哲
日期:2025年5月13日



ZeroJudge 題目連結:k866. 8.運送隕石 (Delivery)

解題想法


我用 priority queue 儲存隕石重量,每次處理最重的隕石,將重量除以 2 之後再放回 priority queue 之中,處理 m 次之後,priority queue 最上面的資料就是答案。

Python 程式碼


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

for line in sys.stdin:
    n, m = map(int, line.split())  # n 顆隕石,m 顆炸彈
    ws = []  # 每顆隕石的重量,為了將最大值放上面,要加負號
    for w in map(int, input().split()): heapq.heappush(ws, -w)
    for _ in range(m):  # 執行 m 次
        w = -heapq.heappop(ws)  # 取出最大值
        h = w/2  # 除以2
        heapq.heappush(ws, -h)  # 加入兩個 -h
        heapq.heappush(ws, -h)
    print(math.ceil(-ws[0]))


2025年5月12日 星期一

ZeroJudge 解題筆記:k865. 7.幼稚園 (Kindergarten)

作者:王一哲
日期:2025年5月12日



ZeroJudge 題目連結:k865. 7.幼稚園 (Kindergarten)

解題想法


我是用兩層的 for 迴圈,先讀取第 i 個人的身高 h,裡面再用兩個 for 迴圈,分別向左、向右找較高的人,取向左、向右交換次數較少者。這樣的寫法效率不夠高,Python 過不了,C++ 可以通過測試。

Python 程式碼


速度太慢,只能通過 40% 的測資,需要再找到更有效率的寫法。
import sys

for line in sys.stdin:
    n = int(line)  # n 個人
    hs = list(map(int, input().split()))  # 身高
    ans = 0  # 交換次數
    for i, h in enumerate(hs):  # 讀取第 i 個人的身高 h
        toleft = 0  # 向左找較高的人數
        for j in range(i-1, -1, -1):
            if hs[j] > h: toleft += 1
        toright = 0  # 向右找較高的人數
        for j in range(i+1, n):
            if hs[j] > h: toright += 1
        if toleft <= toright: ans += toleft  # 取向左、向右交換次數較少者
        else: ans += toright
    print(ans)


2025年5月11日 星期日

ZeroJudge 解題筆記:k854. P8.簡易數織 (Nonogram)

作者:王一哲
日期:2025年5月11日



ZeroJudge 題目連結:k854. P8.簡易數織 (Nonogram)

解題想法


這題我試著用 dp 計算可能的組合,但是最後一筆測資極大,用 Python 最後一筆測資超時。後來我把題目丢到 Gemini 2.0 Flash,它提供了一個數學化的解法,才能在時限內順利解出來。

Python 程式碼


最後一筆測資超時。
import sys

def solve(w, n, arr):
    mod = 10**9 + 7  # 要取餘數用的整數
    imin = sum(arr) + n - 1  # 黑色區塊總和 + 至少需要的空格數量
    if imin > w:  # 需要的格子數大於 w
        print(0); return  # 印出 0,跳出函式
    dp = [[0]*(n+1) for _ in range(w+1)]  # dp[i][j] 填到第 i 格時放入第 j 段黑色區塊
    for i in range(w+1): dp[i][0] = 1  # 初始化,填滿 0 ~ w 格、沒有黑色區塊,只有一種方式
    for i in range(1, w+1):  # 填滿 1 ~ w 格
        for j in range(1, n+1):  # 放入第 j 段黑色區塊
            block = arr[j-1]  # 黑色區塊長度
            dp[i][j] = (dp[i][j] + dp[i-1][j]) % mod  # 不放這個黑色區塊
            if i >= block:  # 可以放入這個黑色區塊
                if j == 1:  # 第 1 個黑色區塊,前面可以不需要是空格
                    dp[i][j] = (dp[i][j] + dp[i-block][j-1]) % mod
                elif i >= block+1:  # 不是第 1 個黑色區塊,前面必須是空格
                    dp[i][j] = (dp[i][j] + dp[i-block-1][j-1]) % mod
    print(dp[w][n]); return

for line in sys.stdin:
    w, n = map(int, line.split())  # w 個格子,n 段連續的黑色區塊
    arr = list(map(int, input().split()))  # 各段連續黑色區塊的長度
    solve(w, n, arr)


ZeroJudge 解題筆記:l919. P.2 珠寶交易 (Jewel)

作者:王一哲
日期:2025年5月20日



ZeroJudge 題目連結:l919. P.2 珠寶交易 (Jewel)

解題想法


這題理論上用貪心法就能解,有通過範例測資,但是無法送出解答。

Python 程式碼


無法送出解答。
import sys

for line in sys.stdin:
    n = int(line)  # n 天
    buy = float('inf')  # 最低買價,預設為正無窮大
    profit = 0  # 最大利潤,預設為 0
    best_buy_dat, best_sell_day = 0, 0  # 最佳買入、𧶠出時機
    buy_day = 0  # 買入時機
    for i, p in enumerate(map(int, input().split()), start=1):  # 第 i 天,價格 p
        if p < buy:  # 如果當天的價格小於 buy,更新 buy 及 buy_day
            buy = p; buy_day = i
        elif p - buy > profit:  # 價差大於 profit
            profit = p - buy  # 更新 profit
            best_sell_day = i  # 更新 sell_day
            best_buy_day = buy_day  # 更新 best_buy_day
    if profit > 0: print(best_buy_day, best_sell_day)
    else: print(-1)