熱門文章

2025年4月30日 星期三

ZeroJudge 解題筆記:k515. P3.標題 (Title)

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



ZeroJudge 題目連結:k515. P3.標題 (Title)

解題想法


我先將所有要小寫的冠詞、介系詞、連接詞存入一個陣列或集合中,並將讀進來的字串全部轉成小寫字母存入陣列,由陣列依序讀取每個單字,如果是整行的開頭或結束則將開頭字母大寫;如果是中間的單字,再檢查這個單字是否為冠詞、介系詞、連接詞,如果不是,則將開頭字母改成大寫。

Python 程式碼


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

article = ("the", "a", "an", "in", "on",
           "at", "of", "for", "by", "to",
           "and", "or", "but")  # 冠詞、介系詞、連接詞
for line in sys.stdin:
    word = line.lower().split()  # 全部轉成小寫,用空格分隔,存成串列
    n = len(word)  # 單字數量
    for i in range(n):  # 依序讀取單字
        if i == 0 or i == n-1:  # 開頭或結尾
            word[i] = word[i][0].upper() + word[i][1:]
        elif word[i] not in article:  # 如果不是開頭或結尾,且 word[i] 不在 article 之中
                word[i] = word[i][0].upper() + word[i][1:]
    print(*word)


2025年4月29日 星期二

ZeroJudge 解題筆記:k514. P2.解藥 (Medicine)

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



ZeroJudge 題目連結:k514. P2.解藥 (Medicine)

解題想法


每組測資只有單筆。先找最小的比例數值,以這種材料的量為基準,檢查外三項材料的比例。

Python 程式碼


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

for line in sys.stdin:
    ratio = list(map(int, line.split()))  # 4 㮔材料的比例
    imin = min(ratio)  # 最小的比例數值
    pmin = ratio.index(imin)  # 最小的比例索引值
    n = int(input())  # 每種材料各有 n 小包
    abcd = [list(map(int, input().split())) for _ in range(4)]  # 每小包的份量
    solve = False  # 是否有解
    if pmin == 0:  # 以第 0 種材料為基準
        for v in abcd[pmin]:  # 依序讀取第 0 種材料每小包的分量,檢查另外 3 項材料的比例
            if v*ratio[1] in abcd[1] and v*ratio[2] in abcd[2] and v*ratio[3] in abcd[3]:
                print(f"{v:d} {v*ratio[1]:d} {v*ratio[2]:d} {v*ratio[3]:d}")
                solve = True; break
    elif pmin == 1:  # 以第 1 種材料為基準
        for v in abcd[pmin]:  # 依序讀取第 1 種材料每小包的分量,檢查另外 3 項材料的比例
            if v*ratio[0] in abcd[0] and v*ratio[2] in abcd[2] and v*ratio[3] in abcd[3]:
                print(f"{v*ratio[0]:d} {v:d} {v*ratio[2]:d} {v*ratio[3]:d}")
                solve = True; break
    elif pmin == 2:  # 以第 2 種材料為基準
        for v in abcd[pmin]:  # 依序讀取第 2 種材料每小包的分量,檢查另外 3 項材料的比例
            if v*ratio[0] in abcd[0] and v*ratio[1] in abcd[1] and v*ratio[3] in abcd[3]:
                print(f"{v*ratio[0]:d} {v*ratio[1]:d} {v:d} {v*ratio[3]:d}")
                solve = True; break
    else:  # 以第 3 種材料為基準
        for v in abcd[pmin]:  # 依序讀取第 3 種材料每小包的分量,檢查另外 3 項材料的比例
            if v*ratio[0] in abcd[0] and v*ratio[1] in abcd[1] and v*ratio[2] in abcd[2]:
                print(f"{v*ratio[0]:d} {v*ratio[1]:d} {v*ratio[2]:d} {v:d}")
                solve = True; break
    if not solve: print(-1)  # 無解,印出 -1


2025年4月28日 星期一

ZeroJudge 解題筆記:k513. P1.停車場 (Parking)

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



ZeroJudge 題目連結:k513. P1.停車場 (Parking)

解題想法


寫比較式的時候,從比較嚴格的條件開始寫,依序檢查是否能停在小停車格,如果不行才再檢查是否能停在中停車格,如果還是不行才再檢查是否能停在大停車格。

Python 程式碼


使用時間約為 22 ms,記憶體約為 3.3 MB,通過測試。
S, M, L = map(int, input().split())  # 小、中、大停車格數量
N = int(input())  # N 輛車
cars = sorted(list(map(int, input().split())))  # 車的大小,由小到大排序
tot = 0  # 可停的車子數量
for car in cars:  # 依序讀取車的大小
    if 1 <= car <= 199:  # 可以停在小停車格
        if S > 0:  # 如果小停車格有剩
            S -= 1; tot += 1
        elif M > 0:  # 如果沒有小停車格,改停中停車格
            M -= 1; tot += 1
        elif L > 0:  # 如果也沒有中停車格,改停大停車格
            L -= 1; tot += 1
    elif 200 <= car <= 499:  # 可以停在中停車格
        if M > 0:  # 如果中停車格有剩
            M -= 1; tot += 1
        elif L > 0:  # 如果沒有中停車格,改停大停車格
            L -= 1; tot += 1
    elif car >= 500 and L > 0:  # 可以停在大停車格
        L -= 1; tot += 1
    else: break  # 其它狀況,沒有停車格了,中止迴圈
print(tot)


2025年4月27日 星期日

ZeroJudge 解題筆記:k468. 打靶 (Target)

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



ZeroJudge 題目連結:k468. 打靶 (Target)

解題想法


我是用字典及 deque 儲存各類型的靶的位置,如果已經打掉就移除資料。

Python 程式碼


使用時間約為 32 ms,記憶體約為 5.8 MB,通過測試。
import sys
from collections import defaultdict, deque

for line in sys.stdin:
    n, s, f = map(int, line.split())  # n 個靶,要打第 s 個 f 類型標靶
    target = list(map(int, input().split()))  # 各位置標靶類型
    state = [True]*n  # 標靶是否還在
    pos = defaultdict(deque)  # 各類型標靶的位置
    for i, t in enumerate(target): pos[t].append(i)
    idx, cnt, hit = 0, 0, 0  # 目前要打的靶位置,射擊次數,打到目標類型標靶數量
    while idx < n:  # 當 idx 還沒有出界
        while not state[idx]: idx += 1  # 如果這個靶已經打掉,找下一個靶
        cnt += 1  # 射擊次數加 1
        now = target[idx]  # 目前打的標靶類型
        state[idx] = False  # 已打掉
        if now == f: hit += 1  # 打到 f 類型標靶,hit 加 1
        pos[now].popleft()  # 移除 now 對應的左側標靶位置
        if pos[now]:  # 如果 now 類型還有標靶
            state[pos[now].popleft()] = False  # 再移除最左側標靶位置,已打掉
            if now == f: hit += 1  # 打到 f 類型標靶,hit 加 1
        if hit >= s:  # 如果已打掉至少 s 個 f 類型標靶
            print(cnt); break  # 印出 cnt,中止迴圈


2025年4月26日 星期六

ZeroJudge 解題筆記:k467. 分班 (Class)

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



ZeroJudge 題目連結:k467. 分班 (Class)

解題想法


這題要同時比較同一個人的語文及數理成績,在 Python 可以用 zip 將兩行資料合併再一起輸出,在 C++ 則可以先儲存語文成績在一個陣列之中,再依序讀取每個人的數理成績,不需要儲存,可以少用一點記憶體。

Python 程式碼


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

for line in sys.stdin:
    n = int(line)  # n 個人
    lan, sci = [], []  # 語文班、數理班座號
    # 讀取兩行資料,用 zip 綁在一起輸出,兩個分數 a, b,編號從 1 開始,
    for i, (a, b) in enumerate(zip(map(int, input().split()), map(int, input().split())), start=1):
        if a > b: lan.append(i)  # a 大,i 加入 lan
        else: sci.append(i)  # b 大,i 加入 sci
    if lan: print(*lan)
    else: print(-1)
    if sci: print(*sci)
    else: print(-1)


2025年4月25日 星期五

ZeroJudge 解題筆記:k466. 成績分析 (Analysis)

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



ZeroJudge 題目連結:k466. 成績分析 (Analysis)

解題想法


依序比較每個人的分數變化即可。

Python 程式碼


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

for line in sys.stdin:
    n, m = map(int, line.split())  # n 個人,m 個分數
    improve, drop = 0, 0  # 進步、退步名單
    imax, dmax = 0, 0  # 進步、退步分數最大值
    for i in range(1, n+1):  # 讀取座號 1 ~ n
        score = list(map(int, input().split()))  # 分數
        im, dm = 0, 0  # 這個人進步(正值)、退步的分數(負值)
        for j in range(1, m):  # 比較 score[j] - score[j-1]
            im += max(score[j] - score[j-1], 0)  # 取分差及 0 較大者加到 im
            dm += min(score[j] - score[j-1], 0)  # 取分差及 0 較小者加到 im
        if im > imax: improve = i; imax = im  # im 較大,更新 improve, imax
        if -dm > dmax: drop = i; dmax = -dm  # -dm 較大,更新 drop, dmax
    print(improve); print(drop)  # 印出答案


2025年4月24日 星期四

ZeroJudge 解題筆記:k399. 取貨 (Pickup)

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



ZeroJudge 題目連結:k399. 取貨 (Pickup)

解題想法


這題用排序工具及 Python 的 tuple 或是 C++ 的 pair 會比較好寫。

Python 程式碼


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

for line in sys.stdin:
    cnt = dict()  # 記錄每個人最後一次登記的編號
    for i, c in enumerate(line.strip()): cnt[c] = i
    order = sorted([(v, k) for k, v in cnt.items()])  # 依照編號排序
    ans = sorted([(k, i) for i, (_, k) in enumerate(order, start=1)])  # 依照人的代號排序
    print("".join([str(i) for _, i in ans]))  # 將每個人對應的序號接成字串印出


2025年4月23日 星期三

ZeroJudge 解題筆記:k398. 密室逃脫 (Escape)

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



ZeroJudge 題目連結:k398. 密室逃脫 (Escape)

解題想法


這題用 BFS 並且在地圖邊緣加一圈不能通過的符號作為哨兵,省略檢查邊界值的判斷式,程式碼會比較好寫。

Python 程式碼


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

for line in sys.stdin:
    m, n = map(int, line.split())  # 地圖 m*n
    step = input().strip()  # 移動步數
    grid = [list('.'*n) + ['#'] for _ in range(m)]  # 地圖,先填入 .,最後加入 # 當作哨兵
    grid.append(list('#'*(n+1)))  # 最後加上 n+1 個 # 當作哨兵
    d = 0  # 方向 0 ~ 3,依序為右、下、左、上
    dr, dc = (0, 1, 0, -1), (1, 0, -1, 0)  # 移動格數
    r, c = 0, 0  # 起點在左上角
    grid[0][0] = '*'  # 起點改成 *
    for s in step:  # 依序讀取移動步數
        s = int(s)  # 轉成整數
        for _ in range(s):  # 走 s 格
            nr, nc = r+dr[d], c+dc[d]  # 要走的格子
            if grid[nr][nc] == '#': break  # 如果走到 #,出界,停在原地
            grid[nr][nc] = '*'  # 走過的格子改成 *
            r, c = nr, nc  # 更新 r, c
        d = (d+1)%4  # 修改方向
    for row in grid[:-1]:  # 印出地圖,不包含最後一列
        print("".join(row[:-1]))  # 不包含最後一格,組成字串再印出


2025年4月22日 星期二

ZeroJudge 解題筆記:k397. 會議安排 (Meeting)

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



ZeroJudge 題目連結:k397. 會議安排 (Meeting)

解題想法


解題時要注意,測資中某些時段會相連,例如輸入範例 4 的 [72, 147] 及 [147, 161],要合併為 [72, 161]。

Python 程式碼


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

for line in sys.stdin:
    n, m = map(int, line.split())  # 兩個人有空的時段數量
    a, b = [], []  # 阿明、阿文有空的時段
    for _ in range(n):  # 讀取 n 行資料
        s, e = map(int, input().split())  # 開始、結束時間 [s, e]
        if a and s == a[-1][1]: a[-1][1] = e  # 如果 a 有資料而且 s 等於最後一個結束時間,更新為 e
        else: a.append([s, e])  # 否則將 [s, e] 加入 a
    for _ in range(m):  # 讀取 m 行資料
        s, e = map(int, input().split())  # 開始、結束時間 [s, e]
        if b and s == b[-1][1]: b[-1][1] = e  # 如果 b 有資料而且 s 等於最後一個結束時間,更新為 e
        else: b.append([s, e])  # 否則將 [s, e] 加入 b
    d = int(input())  # 需要的時間長度
    n, m = len(a), len(b)  # 更新 n, m
    i, j, can = 0, 0, False  # 從 a, b 讀取資料的索引值,是否有解
    while i < n and j < m:  # 當 i, j 都還沒有出界
        if a[i][1] < b[j][0]: i += 1  # a 的結束時間小於 b 的開始時間,i 加 1
        elif b[j][1] < a[i][0]: j += 1  # b 的結束時間小於 a 的開始時間,j 加 1
        elif a[i][0] <= b[j][0] <= a[i][1] or b[j][0] <= a[i][0] <= b[j][1]:  # a, b 有交集
            s, e = max(a[i][0], b[j][0]), min(a[i][1], b[j][1])  # 開始時間取大的,結束時間取小的
            if e - s >= d:  # 如果交集時間夠長,印出 s, s+d,有解,中止迴圈
                print(s, s+d); can = True; break
            if a[i][1] <= b[j][1]: i += 1  # 如果 a 的結束時間較早,i 加 1
            else: j += 1  # 反之,j 加 1
    if not can: print(-1)  # 如果無解,印出 -1


2025年4月21日 星期一

ZeroJudge 解題筆記:k255. 鑿井取水 (Water)

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



ZeroJudge 題目連結:k255. 鑿井取水 (Water)

解題想法


解題時要注意,水量為 0 不算枯竭。

Python 程式碼


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

for line in sys.stdin:
    n, w = map(int, line.split())  # n 天,每天産生 w 單位的水
    tot = 0  # 水量
    dry = False  # 是否已經枯竭
    for i in range(1, n+1):  # 檢查第 1 ~ n 天
        used = sum(list(map(int, input().split()))[1:])  # 用水量
        tot = tot + w - used  # 更新水量
        if not dry and tot < 0:
            print(i); dry = True
    if not dry: print(-1)


2025年4月20日 星期日

ZeroJudge 解題筆記:k254. 拍七 (Seven)

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



ZeroJudge 題目連結:k254. 拍七 (Seven)

解題想法


將整數轉成字串,在檢查字串中是否有指定的數字時會比較方便。

Python 程式碼


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

for line in sys.stdin:
    s, e, b, k = map(int, line.split())
    cnt = 0  # 拍手次數
    for i in range(s, e+1):  # 依序檢測 s ~ e
        if i%b == 0 or str(b) in str(i): cnt += 1  # i 轉成字串檢查之中是否有 b
        if cnt == k:
            print(i); break
    if cnt < k: print(-1)


2025年4月19日 星期六

ZeroJudge 解題筆記:k253. 庫存清理 (Stock)

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



ZeroJudge 題目連結:k253. 庫存清理 (Stock)

解題想法


有比較簡單、直接但行數很多的寫法,也可以用 for 迴圈合併重複的部分。

Python 程式碼


簡單直接但是行數很多的寫法,使用時間約為 19 ms,記憶體約為 3.4 MB,通過測試。
import sys

for line in sys.stdin:
    t, s, p = map(int, line.split())  # 商品初始數量、來店顧客數、商品單價
    d = t//5  # 1/5 的商品數量
    tot = 0  # 總金額
    while t >= d and s > 0:  # 還有可販售商品,還有顧客
        if t >= 4*d:  # 商品數量大於等於 4*d
            if s <= d+1:  # 人數小於等於 d+1
                tot += s*(p*5//10); s = 0; t -= s
            else:  # 人數大於 d+1
                tot += (d+1)*(p*5//10); s -= d+1; t -= d+1
        elif t >= 3*d:  # 商品數量大於等於 3*d
            if s <= d:  # 人數小於等於 d
                tot += s*(p*6//10); s = 0; t -= s
            else:  # 人數大於 d
                tot += d*(p*6//10); s -= d; t -= d
        elif t >= 2*d:  # 商品數量大於等於 2*d
            if s <= d:  # 人數小於等於 d
                tot += s*(p*8//10); s = 0; t -= s
            else:  # 人數大於 d
                tot += d*(p*8//10); s -= d; t -= d
        elif t >= d:  # 商品數量大於等於 d
            if s <= d:  # 人數小於等於 d
                tot += s*(p*9//10); s = 0; t -= s
            else:  # 人數大於 d
                tot += d*(p*9//10); s -= d; t -= d
    print(tot)

因為以上的程式碼有許多重複的地方,可以簡化成以下的樣子,使用時間約為 18 ms,記憶體約為 3.3 MB,通過測試。
import sys

dis = (5, 6, 8, 9)  # 折扣
for line in sys.stdin:
    t, s, p = map(int, line.split())  # 商品初始數量、來店顧客數、商品單價
    d, tot, i = t//5, 0, 0  # 1/5 的商品數量,總金額,索引值
    while i < 4 and s > 0:  # i 小於 4,還有顧客
        n = d + (i==0)  # 這次最多可以販賣的數量,若 i 等於 0 時 n = d+1,否則 n = d
        tot += min(s, n)*(p*dis[i]//10); s -= min(s, n); t -= n; i += 1  # 數量取 s, n 較小值
    print(tot)


2025年4月18日 星期五

ZeroJudge 解題筆記:j538. 賣場設置 (Market)

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



ZeroJudge 題目連結:j538. 賣場設置 (Market)

解題想法


我是用字典儲存商品位置及數量,寫起來比較簡單。

Python 程式碼


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

for s in sys.stdin:
    s = s.strip().lower()  # 商品種類,去掉結尾的 \n 再轉成小寫
    t = input().strip()  # 商品數量,去掉結尾的 \n
    pos = defaultdict(list)  # 各商品種類所在的位置
    val = defaultdict(int)  # 各商品種類的數量
    for i, (k, v) in enumerate(zip(s, t)):  # 將 s, t 綁在一起輸出
        pos[k].append(i)  # 商品 k 的位置新增 i
        val[k] += int(v)  # 商品 k 的數量加 v
    ans = [0]*len(s)  # 記錄答案用的串列
    for k, p in pos.items():  # 依序讀取 pos 的 key, value
        a = val[k]//len(p)  # 平均數量
        r = val[k]%len(p)  # 餘數
        for i, x in enumerate(p[::-1]):  # 從 p 尾端往回讀取位置
            if i < r: ans[x] = a+1  # 後面 r 個位置數量為 a+1
            else: ans[x] = a  # 其它位置數量為 a
    print(*ans, sep="")  # 將 ans 拆開、印出,不加分隔符號


2025年4月17日 星期四

ZeroJudge 解題筆記:j537. 工廠派遣 (Factory)

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



ZeroJudge 題目連結:j537. 工廠派遣 (Factory)

解題想法


這題將每間工廠的産值 a、需要的人數 p,綁在一起放入陣列,依照 v 由大到小排序會比較方便。

Python 程式碼


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

for line in sys.stdin:
    n = int(line)  # n 間工廠
    # 讀取每間工廠的産值 a、需要的人數 p,組成 tuple、放入 list,依照 v 由大到小排序
    ap = sorted([tuple(map(int, input().split())) for _ in range(n)], reverse=True)
    h = int(input())  # 總人數
    cnt = 0  # 有分配到人的工廠數量
    idx = 0  # 從 ap 讀取資料的索引值
    while idx < n and h > 0:  # 如果 idx 還沒有出界而且還有人,繼續執行
        cnt += 1; h -= ap[idx][1]; idx += 1  # cnt 加 1,h 減去此工廠的人數,idx 加 1
    print(cnt)


2025年4月16日 星期三

ZeroJudge 解題筆記:j536. 不穩定的礦石 (Ore)

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



ZeroJudge 題目連結:j536. 不穩定的礦石 (Ore)

解題想法


分成兩次檢查吸收能量的狀況比較好寫。

Python 程式碼


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

for line in sys.stdin:
    n, a = map(int, line.split())  # n 個礦石,吸收力 a
    stone = list(map(int, input().split()))  # 礦石原來的能量
    p = stone.index(max(stone))  # 最多能量的礦石位置
    isum = sum(stone)  # 總能量
    cnt, imax = 0, stone[p]  # 已吸收的數量,吸收後的能量
    le = p - 1  # 向左吸收的位置
    # 向左吸收,最多吸收到位置 0 或是 p-a//2 為止,cnt 一定小於 a
    while le >= 0 and le >= p-a//2:
        cnt += 1; imax += stone[le]; le -= 1
    ri = p + 1  # 向右吸收的位置
    # 向右吸收,最多吸收到位置 n-1 或是 p+a//2 為止,cnt 一定小於 a
    while ri < n and ri <= p+a//2:
        cnt += 1; imax += stone[ri]; ri += 1
    # 第二次向左吸收,最多吸收到 cnt 等於 a 或是位置 0 
    while cnt < a and le >= 0:
        cnt += 1; imax += stone[le]; le -= 1
    # 第二次向右吸收,最多吸收到 cnt 等於 a 或是位置 n-1
    while cnt < a and ri < n:
        cnt += 1; imax += stone[ri]; ri += 1
    print(imax, isum-imax)  # 印出答案


2025年4月15日 星期二

ZeroJudge 解題筆記:j354. 積木對接 (Blocks)

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



ZeroJudge 題目連結:j354. 積木對接 (Blocks)

解題想法


這題將判斷兩個積木是否有解的函式另外寫會比較方便。

Python 程式碼


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

def solve(bot, top):  # 輸入兩個積木測試是否有解
    n, m = len(bot), len(top)
    if m > n:  # 特例,上方的積木較長,無解
        print("NO"); return
    d = n - m  # 長度差
    for i in range(d+1):  # 短積木的左側位置可能是 0 ~ d
        if all([t&b == 0 for t, b in zip(top, bot[i:i+m])]):
            print("YES"); return
    print("NO"); return

for line in sys.stdin:
    n = int(line)  # 長積木的卡榫數量
    bot = []  # 下方的長積木每格的高度,0 代表低,1 代表高
    h = 0  # 高度,長積木由 0 開始
    for b in map(int, input().split()):  # 依序設定長積木每格的高度
        for _ in range(b): bot.append(h)  # 放入 b 個 h
        h = 1 - h  # 0 變 1 或 1 變 0
    m = int(input())  # 短積木的卡榫數量
    top = []  # 上方的短積木每格的高度,0 代表低,1 代表高
    h = 1  # 高度,短積木由 1 開始
    for t in map(int, input().split()):  # 依序設定短積木每格的高度
        for _ in range(t): top.append(h)  # 放入 t 個 h
        h = 1 - h  # 0 變 1 或 1 變 0
    solve(bot, top)


2025年4月14日 星期一

ZeroJudge 解題筆記:j352. 開心農場 (Farm)

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



ZeroJudge 題目連結:j352. 開心農場 (Farm)

解題想法


依序讀取資料並判斷是否能開花即可。

Python 程式碼


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

for line in sys.stdin:
    n = int(line)  # 週數
    rain = list(map(int, input().split()))  # 雨量
    sun = list(map(int, input().split()))  # 日照
    R, S = map(int, input().split())  # 所需總雨量、日照
    tr, ts, state = 0, 0, False  # 累積的雨量、日照、是否開花
    for i, (r, s) in enumerate(zip(rain, sun), start=1):
        tr += r; ts += s  # 更新累積的雨量、日照
        if tr >= R and ts >= S:  # 達到開花條件,印出 i+1,中止迴圈
            print(i+1); state = True; break
    if not state: print(-1)  # 不能開花,印出 -1


2025年4月13日 星期日

ZeroJudge 解題筆記:j180. 戰備存糧 (Food)

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



ZeroJudge 題目連結:j180. 戰備存糧 (Food)

解題想法


這題如果按照題目的敘述,先將每個倉庫的存糧減 1,再將後方倉庫的存糧往前塞,這樣會跑很慢,一定無法通過測試。既然題目要將存糧盡量往前塞,那就乾脆計算存糧的數量,如果能用更少的倉庫裝下存糧,那就減一個倉庫。

Python 程式碼


使用時間約為 0.2 s,記憶體約為 3.3 MB,通過測試。
while True:
    line = input().strip()
    if line == "-1": break
    n, e = map(int, line.split())
    tot = n*e  # 存糧總量
    day = 0  # 天數
    while tot > 0:  # 還有存糧
        day += 1  # 天數加 1
        tot -= n  # 存糧減 n
        while tot <= (n-1)*e:  # 如果剩下的存糧可以用 (n-1) 個倉庫裝下
            n -= 1  # 減 1 個倉庫
    print(day)


2025年4月12日 星期六

ZeroJudge 解題筆記:j179. 資料分類 (Classification)

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



ZeroJudge 題目連結:j179. 資料分類 (Classification)

解題想法


注意:n 的位數在運算時不一定會遞減。

Python 程式碼


利用 int 將字串轉成整數,刪除前面的 0,再用 str 轉回字串。使用時間約為 19 ms,記憶體約為 3.3 MB,通過測試。
import sys

for n in sys.stdin:
    n = n.strip()
    while len(n) > 1:  # 如果 n 長度大於 1 繼續執行
        if len(n) == 4:  # 4 位數
            le, ri = n[:2], n[2:]  # 左、右半邊
            if int(le) >= 10:  # 轉成 int,如果大於等於 10 才要相乘
                le = str(int(le[0])*int(le[1]))  # 兩位轉成 int 相乘再轉回 str
            if int(ri) >= 10:  # 轉成 int,如果大於等於 10 才要相乘
                ri = str(int(ri[0])*int(ri[1]))  # 兩位轉成 int 相乘再轉回 str
            else: ri = str(int(ri))  # 去掉前面的 0
            n = str(int(le + ri))  # 先將 le + ri 轉成 int 再轉回 str,去掉前面的 0
        elif len(n) == 3:  # 3 位數
            le, ri = n[:2], n[1:]  # 左、右半邊
            le = str(int(le[0])*int(le[1]))  # 兩位轉成 int 相乘再轉回 str
            ri = str(int(ri[0])*int(ri[1]))  # 兩位轉成 int 相乘再轉回 str
            n = str(int(le + ri))  # 先將 le + ri 轉成 int 再轉回 str,去掉前面的 0
        elif len(n) == 2:  # 2 位數
            n = str(int(n[0])*int(n[1]))  # 兩位轉成 int 相乘再轉回 str
    print(n)


2025年4月11日 星期五

ZeroJudge 解題筆記:i378. 垂直對稱 (Symmetry)

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



ZeroJudge 題目連結:i378. 垂直對稱 (Symmetry)

解題想法


相同的 x 座標上如果有奇數個點,則 y 座標正中央的點必須在水平對稱線上,而且只能有 1 個值。接著再檢查一次,相同的 x 座標上所有的點 y 座標和,必須等於水平對稱線的 y 座標乘以點的數量。

Python 程式碼


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

for line in sys.stdin:
    n = int(line)  # n 個點
    ps = dict()  # x 座標: [y1, y2, ...]
    for _ in range(n):  # 讀取 n 個點
        x, y = map(int, input().split())
        if x not in ps: ps[x] = [y]
        else: ps[x].append(y)
    cand = 10000  # 水平對稱線可能的 y 座標,預設為超出題目範圍的值
    found = True  # 是否有解
    for _, ys in ps.items():  # 找相同的 x 座標中所有點的 y 座標
        ys.sort()  # 要排序
        if len(ys)%2 == 1:  # 如果有奇數個點的項目
            if cand == 10000: cand = ys[len(ys)//2]  # 如果 cand 是預設值,更新
            elif cand != ys[len(ys)//2]:  # 不相同
                found = False; break  # 無解
    if found:  # 如果只有一個 cand
        for _, ys in ps.items():  # 找相同的 x 座標中所有點的 y 座標
            if cand*len(ys) != sum(ys):  # 如果不相等,cand 不在這些點的正中央
                found = False; break  # 無解
    print("success" if found else "failure")


2025年4月10日 星期四

ZeroJudge 解題筆記:i377. 單字找找看 (Word)

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



ZeroJudge 題目連結:i377. 單字找找看 (Word)

解題想法


這題寫起來很長,因為要找 8 個方向,依照題目對於答案的要求,搜尋的方向依序是左上、正上、右上、正左、正右、左下、正下、右下。

Python 程式碼


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

def solve(g, t):  # 輸入字串方格 g,找目標字串 t
    m, n = len(g) -1, len(g[0]) -1  # 列數減 1,每列長度減 1
    tl = len(t)  # t 的長度
    for r in range(m):  # 依序掃過每列
        for c in range(n):  # 依序掃過每欄
            found = True  # 是否找到答案,先找左上方
            for i in range(tl):
                if t[i] != grid[r-i][c-i]:  # 只要字元不相等就可以中止迴圈
                    found = False; break
            if found:  # 如果找到答案,印出起點、終點,記得加 1
                print(f"{r+1:d} {c+1:d}\n{r-tl+2:d} {c-tl+2:d}")
                return  # 中止函式
            found = True  # 是否找到答案,找正上方
            for i in range(tl):
                if t[i] != grid[r-i][c]:
                    found = False; break
            if found:
                print(f"{r+1:d} {c+1:d}\n{r-tl+2:d} {c+1:d}")
                return
            found = True  # 是否找到答案,找右上方
            for i in range(tl):
                if t[i] != grid[r-i][c+i]:
                    found = False; break
            if found:
                print(f"{r+1:d} {c+1:d}\n{r-tl+2:d} {c+tl:d}")
                return
            found = True  # 是否找到答案,找正左方
            for i in range(tl):
                if t[i] != grid[r][c-i]:
                    found = False; break
            if found:
                print(f"{r+1:d} {c+1:d}\n{r+1:d} {c-tl+2:d}")
                return
            found = True  # 是否找到答案,找正右方
            for i in range(tl):
                if t[i] != grid[r][c+i]:
                    found = False; break
            if found:
                print(f"{r+1:d} {c+1:d}\n{r+1:d} {c+tl:d}")
                return
            found = True  # 是否找到答案,找左下方
            for i in range(tl):
                if t[i] != grid[r+i][c-i]:
                    found = False; break
            if found:
                print(f"{r+1:d} {c+1:d}\n{r+tl:d} {c-tl+2:d}")
                return
            found = True  # 是否找到答案,找正下方
            for i in range(tl):
                if t[i] != grid[r+i][c]:
                    found = False; break
            if found:
                print(f"{r+1:d} {c+1:d}\n{r+tl:d} {c+1:d}")
                return
            found = True  # 是否找到答案,找右下方
            for i in range(tl):
                if t[i] != grid[r+i][c+i]:
                    found = False; break
            if found:
                print(f"{r+1:d} {c+1:d}\n{r+tl:d} {c+tl:d}")
                return
    print("NO"); return

for line in sys.stdin:
    m, n = map(int, line.split())  # 表格 m*n
    grid = [input().strip().lower()+"0" for _ in range(m)]  # 加 0 作為哨兵
    grid.append("0"*(n+1))
    solve(grid, input().strip().lower())


2025年4月9日 星期三

ZeroJudge 解題筆記:i376. 尋寶 (Treasure)

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



ZeroJudge 題目連結:i376. 尋寶 (Treasure)

解題想法


我是先掃過每一列找出最高處,將這些位置的座標加入一個集合 cand 之中;再掃過每一欄,找每欄的最低點,如果此處的座標在 cand 之中,表示已經找到答案。

Python 程式碼


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

for line in sys.stdin:
    n = int(line)  # 地圖 n*n
    grid = [list(map(int, input().split())) for _ in range(n)]  # 地圖資料
    cand = []  # 可能的地點
    for r in range(n):  # 先找每列最高處放入 cand,可能有多個最高處
        imax = max(grid[r])  # 第 r 列的最大值
        for c in range(n):  # 依序掃過每欄
            if grid[r][c] == imax:  # 如果此格等於 imax
                cand.append((r, c))  # (r, c) 加入 cand
    found = False  # 是否找到答案
    for c in range(n):  # 依序找每欄的最低處在第幾列
        imin = min([grid[i][c] for i in range(n)])  # 第 c 欄的最小值
        for r in range(n):  # 依序掃過每列
            if grid[r][c] == imin and (r, c) in cand:  # 如果此格等於 imin 而且在 cand 之中
                found = True  # 找到答案
                print(r, c)  # 印出 (r, c)
                break  # 中止迴圈
        if found: break  # 如果已經找到答案,中止迴圈    
    if not found: print("NO")  # 如果沒有找到答案印出 NO


2025年4月8日 星期二

ZeroJudge 解題筆記:i164. 比對卡片(進階版)

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



ZeroJudge 題目連結:i164. 比對卡片(進階版)

解題想法


題目的敘述是:對老師的每張卡片,孩童需找到自己的卡片中和老師卡片數字相同且位置差距最小的卡片。看起來應該是孩童依照自己手上的卡片,依序找每張卡片的數字是否有在老師手上的卡片之中,如果有則要找距離最近的卡片並記錄位置差;但如果以題目中範例的圖示來看則是反過來,老師依照自己手上的卡片,依序找每張卡片的數字是否有在孩童手上的卡片之中,如果有則要找距離最近的卡片並記錄位置差。 這題的基礎版為〈i070. 比對卡片 (Match)〉,測資小很多,用基本的寫法就能通過,但是這題不行。要從左向右、從右向左各掃過一次,記錄 a[i] 前一次出現的位置,如果 a[i] 已經出現過就更新距離。

Python 程式碼


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

for line in sys.stdin:
    n = int(line)  # 牌的數量
    a = list(map(int, input().split()))  # 老師的牌
    b = list(map(int, input().split()))  # 孩童的牌
    pre = [-1]*1000000  # 每張牌前一次出現的位置,-1 代表還沒有出現過,題目牌面編號最大值為 100000
    ans = [n]*n  # 答案,預設為過大的值 n,因為位置差為 0 ~ n-1
    for i in range(n):  # 依序檢查每張牌
        pre[b[i]] = i  # b[i] 的數字前一次出現為位為 i
        if pre[a[i]] != -1:  # 如果 a[i] 的數字之前已經出現過
            ans[i] = min(ans[i], abs(i - pre[a[i]]))  # 更新位置差
    for i in range(n-1, -1, -1):  # 反過來檢查每張牌
        pre[b[i]] = i  # b[i] 的數字前一次出現為位為 i
        if pre[a[i]] != -1:  # 如果 a[i] 的數字之前已經出現過
            ans[i] = min(ans[i], abs(i - pre[a[i]]))  # 更新位置差
    ans = list(map(lambda x : -1 if x == n else x, ans))  # ans 之中的 n 取代為 -1
    print(*ans)


2025年4月7日 星期一

ZeroJudge 解題筆記:i071. 風景 (Landscape)

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



ZeroJudge 題目連結:i071. 風景 (Landscape)

解題想法


從起點開始向左、向右線性搜尋各一次即可。

Python 程式碼


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

for line in sys.stdin:
    n, m = map(int, line.split())  # n 棟建築,人在第 m 棟,編號從 1 開始
    hs = list(map(int, input().split()))  # 所有建築物的高度
    tot = 0  # 能看到的建築物數量
    m -= 1  # 建築物編號改成從 0 開始
    pre = hs[m]  # 前一棟最高建築物的高度,預設為自己所在的建築物高度
    for i in range(m, 0, -1):  # 從 m 開始向左找
        if hs[i-1] > pre:  # 如果左邊的建築物高度大於 pre
            tot += 1  # tot 加 1
            pre = hs[i-1]  # 更新 pre
    pre = hs[m]  # 重設 pre
    for i in range(m, n-1):  # 從 m 開始向右找
        if hs[i+1] > pre:  # 如果右邊的建築物高度大於 pre
            tot += 1  # tot 加 1
            pre = hs[i+1]  # 更新 pre
    print(tot)  # 印出答案


2025年4月6日 星期日

ZeroJudge 解題筆記:i070. 比對卡片 (Match)

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



ZeroJudge 題目連結:i070. 比對卡片 (Match)

解題想法


題目的敘述是:對老師的每張卡片,孩童需找到自己的卡片中和老師卡片數字相同且位置差距最小的卡片。看起來應該是孩童依照自己手上的卡片,依序找每張卡片的數字是否有在老師手上的卡片之中,如果有則要找距離最近的卡片並記錄位置差;但如果以題目中範例的圖示來看則是反過來,老師依照自己手上的卡片,依序找每張卡片的數字是否有在孩童手上的卡片之中,如果有則要找距離最近的卡片並記錄位置差

另外有一題進階版〈i164. 比對卡片(進階版)〉,測資大很多。

Python 程式碼


寫法1,用串列切片及 index 找索引值。使用時間約為 21 ms,記憶體約為 3.5 MB,通過測試。
import sys

for line in sys.stdin:
    n = int(line)  # 牌的數量
    a = list(map(int, input().split()))  # 老師的牌
    b = list(map(int, input().split()))  # 孩童的牌
    for i in range(n):  # 依序檢查每張牌
        t = a[i]  # 目標
        if t not in b:  # 如果 t 不在 b 之中,印出 -1
            print(-1, end="\n" if i == n-1 else " ")
        elif i == 0:  # 編號 0,只要向右找,索引值等於位置差
            if t not in b: print(-1, end=" ")  # 如果 t 不在 b 之中,印出 -1
            else: print(b.index(t), end=" ")
        elif i == n-1:  # 編號 n-1,只要向左找
            if t not in b: print(-1)  # 如果 t 不在 b 之中,印出 -1
            else: print(b[::-1].index(t))  # 先將 b 反過來再找 t
        else:  # 編號在中間,向右、向左各找一次,取較小的值
            left = b[:i+1][::-1]  # 取 b[0] ~ b[i+1],再反向
            right = b[i:]  # 取 b[i] ~ b[n-1]
            p, q = n, n  # 儲存位置差的變數,先預設為 n
            if t in right: p = right.index(t)  # 向右找
            if t in left: q = left.index(t)  # 向左找
            print(min(p, q), end=" ")  # 印出小的那個

2025年4月5日 星期六

ZeroJudge 解題筆記:i069. 岩石觀察 (Stones)

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



ZeroJudge 題目連結:i069. 岩石觀察 (Stones)

解題想法


因為 Python 會一次讀取整行的資料,存成串列後再找最大值、最小值。C++ 一次讀取一筆資料,可以在讀取所有盒子的資料時一邊找最大值、最小值。

Python 程式碼


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

for line in sys.stdin:
    n = int(line)  # n 個盒子
    box = list(map(int, input().split()))  # 原來的數量
    avg = sum(box)//n  # 平均數量
    pmax = box.index(max(box))  # 數量最多的盒子編號
    pmin = box.index(min(box))  # 數量最少的盒子編號
    d = box[pmax] - avg  # 移動的數量
    box[pmax] -= d  # 減少 d
    box[pmin] += d  # 增加 d
    print(*box)  # 印出答案


2025年4月4日 星期五

ZeroJudge 解題筆記:h660. 躲避球 (DodgeBall)

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



ZeroJudge 題目連結:h660. 躲避球 (DodgeBall)

解題想法


按照題目敘述計算位置即可,不需要複雜的寫法。

Python 程式碼


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

for line in sys.stdin:
    x, r, v = map(int, line.split())  # 阿茂的位置、接球的左右延伸長度、可接球的球速上限
    for _ in range(int(input())):  # 執行 n 次
        p, s = map(int, input().split())  # 球的落點、速度
        if x-r <= p <= x+r:  # 球在可以接到的範圍內
            if s <= v: x = p  # 球速夠慢,可以接球,移動到 p
            else:  # 球速太快,不能接球
                if p >= x: x -= 15  # 球在右邊或朝人過去,向左移 15
                else: x += 15  # 球在左邊,向右移 15
    print(x)  # 印出最後的位置


2025年4月3日 星期四

ZeroJudge 解題筆記:k518. P6.蛋糕切塊 (Cake)

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



ZeroJudge 題目連結:k518. P6.蛋糕切塊 (Cake)

解題想法


這題考動態規畫,我同時利用 set 儲存可用的子字串,這樣程式運作速度會比較快。

Python 程式碼


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

def can_cut_cake(s, cuts):
    n = len(s)  # 字串長度
    dp = [False]*(n+1)  # 是否能走到 i,如果能走到 i+1 代表有解
    dp[0] = True  # 一定能走到開頭
    # 動態規劃遍歷
    for i in range(1, n+1):  # 依序找終點 i = 1 ~ n
        for j in range(i):  # 依序找起點 j = 0 ~ i-1
            if dp[j] and s[j:i] in cuts:  # 如果能走到 j 而且 s[j:i] 在 cuts 之中
                dp[i] = True  # 可以走到 i
                break  # 只要找到一個可行切割後就可以停止
    print("yes" if dp[n] else "no")  # 如果 dp[n] 為 True 印出 yes,反之印出 no

for s in sys.stdin:
    s = s.strip()  # 要檢查的字串
    m = int(input())  # 可用的子字串數量
    cuts = set()  # 可用的子字串
    for _ in range(m): cuts.add(input())  
    can_cut_cake(s, cuts)  # 計算並輸出結果


ZeroJudge 解題筆記:h659. 計程車 (Taxi)

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



ZeroJudge 題目連結:h659. 計程車 (Taxi)

解題想法


按照題目敘述的標準計算費用即可,不需要複雜的寫法。

Python 程式碼


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

for line in sys.stdin:
    # 行駛總公里數、車輛延滯時間 (min)、上車及下車的時刻 (hr)
    k, w, s, e = map(int, line.split())
    fee = 0  # 費用
    # 計算行駛總公里數産生的費用
    if k <= 2: fee += 20  # 2 km 以內 20 元
    else: fee += 20 + (k-2)*5  # 超過 2 km 加錢 5 元/km
    # 計算車輛延滯時間産生的費用
    fee += w//2*5  # 每滿 2 min 加 5 元
    # 計算夜間加成産生的費用
    if s <= 18 and e >= 19: fee += 185  # 橫跨 18 ~ 19 時,加 185 元
    if s <= 19 and e >= 20: fee += 195  # 橫跨 19 ~ 20 時,加 195 元
    if s <= 20 and e >= 21: fee += 205  # 橫跨 20 ~ 21 時,加 205 元
    if s <= 21 and e >= 22: fee += 215  # 橫跨 21 ~ 22 時,加 215 元
    if s <= 22 and e >= 23: fee += 225  # 橫跨 22 ~ 23 時,加 225 元
    print(fee)  # 印出答案


2025年4月2日 星期三

ZeroJudge 解題筆記:h658. 捕魚 (Fishing)

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



ZeroJudge 題目連結:h658. 捕魚 (Fishing)

解題想法


用距離平方判斷是否在範圍內即可,不需要開根號。

Python 程式碼


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

for line in sys.stdin:
    x, y = map(int, line.split())  # 漁夫的座標
    n = int(input())  # n 筆資料
    ax, ay, d = 0, 0, float('inf')  # 最接近的魚群座標、距離的平方
    for _ in range(n):  # 執行 n 次
        xi, yi = map(int, input().split())  # 魚群座標
        di = (x - xi)**2 + (y - yi)**2  # 距離的平方
        if di < d:  # 如果距離較近,更新 ax, ay, d
            ax, ay, d = xi, yi, di
    print(ax, ay)


2025年4月1日 星期二

ZeroJudge 解題筆記:h035. 夜市 (Night Market)

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



ZeroJudge 題目連結:h035. 夜市 (Night Market)

解題想法


注意題目中的這句「但一塊板子只會採計一次得分」,不能重複計分。測資應該只有單筆輸入。

Python 程式碼


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

for line in sys.stdin:
    n = int(line)  # 投球 n 次
    down = [-1]*10  # 各編號的格子被打落的時刻,-1 代表沒有被打落
    hit = [False]*10  # 各編號的格子是否被打落過
    cnt, score, perfect = 0, 0, False  # 目前打落的板子數量,總分,是否全清
    for i, x in enumerate(list(map(int, input().split())), start=1):  # 依序讀取投球命中的格子
        for j in range(1, 10):  # 檢查每塊板子是否能升起
            if down[j] != -1 and i == down[j] + 12:  # 如果第 j 塊板子已被打落而且 i 等於第 j 塊板子被打落的時刻加 12
                down[j] = -1; cnt -= 1  # 升起第 j 塊板子,cnt 減 1
        if x != -1 and down[x] == -1:  # 如果 x 不等於 -1 而且 x 號板子目前沒有被打落
            cnt += 1; down[x] = i  # cnt 加 1,設定 x 被打落的時刻為 i
            if not hit[x]:  # 如果 x 號板子沒有被打落過
                score += x; hit[x] = True  # 分數加 x,x 號板子被打落過
        if cnt == 9:  # 如果 cnt 等於 9,全清,中止迴圈
            perfect = True; break
    print("perfect" if perfect else score)