熱門文章

2025年7月15日 星期二

ZeroJudge 解題筆記:a740. 质因数之和

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


ZeroJudge 題目連結:a740. 质因数之和

解題想法


測試 i = 2 ~ 根號 x 是否為 x 的因數,如果 i 是因數就繼續將 x 縮小為 1/i,直到 i 不是因數為止。最後如果 x 不等於 1,代表此時 x 是某一個質數,答案再加上 x。

Python 程式碼


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

for line in sys.stdin:
    x = int(line)
    ans = 0
    for i in range(2, int(x**0.5)+1):  # 測試 2 ~ 根號 x
        while x%i == 0:  # 如果 i 可以整除 x
            ans += i  # 加上 i
            x //= i  # x 變為 1/i
    if x != 1: ans += x  # 如果 x 不是 1,x 為質數
    sys.stdout.write(f"{ans:d}\n")


2025年7月14日 星期一

ZeroJudge 解題筆記:a694. 吞食天地二

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


ZeroJudge 題目連結:a694. 吞食天地二

解題想法


題目中的 x 軸應該是對應到列,y 軸對應到欄,有點奇怪。解題技巧主要利用的是二維前綴和,産生前綴和的原則
psum[i][j] = psum[i-1][j] + psum[i][j-1] - psum[i-1][j-1]
因為 $psum[i-1][j-1]$ 會被多加一次,最後要扣掉。用前綴和取範圍 $[r1, c1]$ 到 $[r2, c2]$ 區間和的原則
isum = psum[r2][c2] - psum[r1-1][c2] - psum[r2][c1-1] + psum[r1-1][c1-1]
因為 $psum[r1-1][c1-1]$ 會被多扣一次,最後要加回來。

Python 程式碼


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

for line in sys.stdin:
    n, m = map(int, line.split())
    psum = [[0]*(n+1) for _ in range(n+1)]  # 最上面、最左邊補 0
    for i in range(1, n+1):  # 讀取 n 行資料
        row = [0] + list(map(int, sys.stdin.readline().split()))
        for j in range(1, n+1):  # 處理前綴和
            psum[i][j] = psum[i-1][j] + psum[i][j-1] - psum[i-1][j-1] + row[j]
    for _ in range(m):  # 讀取 m 行
        r1, c1, r2, c2 = map(int, sys.stdin.readline().split())
        isum = psum[r2][c2] - psum[r1-1][c2] - psum[r2][c1-1] + psum[r1-1][c1-1]
        sys.stdout.write(f"{isum:d}\n")


2025年7月13日 星期日

ZeroJudge 解題筆記:a693. 吞食天地

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


ZeroJudge 題目連結:a693. 吞食天地

解題想法


用前綴和解題,如果要求陣列中索引值 [left, right] 的區間和,即為前綴和 psum[right] - psum[left-1]。

Python 程式碼


逐行讀取測資、輸出結果,使用時間約為 0.5 s,記憶體約為 11.8 MB,通過測試。如果改用 print 輸出答案,使用時間約為 1.2 s,記憶體約為 11.7 MB。
import sys

for line in sys.stdin:
    n, m = map(int, line.split())
    psum = [0] + list(map(int, sys.stdin.readline().split()))
    for i in range(1, n+1):  # 轉成前綴和
        psum[i] += psum[i-1]
    for _ in range(m):  # 讀取 m 行
        left, right = map(int, sys.stdin.readline().split())
        isum = psum[right] - psum[left-1]
        sys.stdout.write(f"{isum:d}\n")

一次讀取所有測資,最後再一次輸出所有結果,使用時間約為 0.5 s,記憶體約為 36.2 MB,通過測試。
import sys

result = []
lines = sys.stdin.readlines()
idx = 0
while idx < len(lines):
    n, m = map(int, lines[idx].split())
    idx += 1
    psum = [0] + list(map(int, lines[idx].split()))
    idx += 1
    for i in range(1, n+1):  # 轉成前綴和
        psum[i] += psum[i-1]
    for _ in range(m):  # 讀取 m 行
        left, right = map(int, lines[idx].split())
        idx += 1
        isum = psum[right] - psum[left-1]
        result.append(f"{isum:d}\n")
sys.stdout.write("".join(result))


2025年7月12日 星期六

ZeroJudge 解題筆記:a248. 新手訓練 ~ 陣列應用

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


ZeroJudge 題目連結:a248. 新手訓練 ~ 陣列應用

解題想法


理論上這題是要先計算 a, b 相除的商,接著再取 a %= b,每次將 a 乘以 10 倍之後除以 b,計算小數的部分直到 n 位數為止,但是這個作法在 Python 會太慢。反而可以利用 Python 支援大數運算的特性,直接將 a%b 乘以 $10^n$ 再除以 b 計算小數的部分,但是要記得在小數部分前面補 0 到 n 位數。

Python 程式碼


寫法1,超時。
import sys

result = []
lines = sys.stdin.readlines()
for line in lines:
    if not line.rstrip(): continue
    a, b, n = map(int, line.split())
    int_part = a//b
    a %= b
    res = [f"{int_part:d}."]
    for _ in range(n):
        a *= 10
        c = a//b
        res.append(f"{c:d}")
        a %= b
    result.append("".join(res) + "\n")
sys.stdout.write("".join(result))

寫法2,大數運算,使用時間約為 0.5 s,記憶體約為 17.2 MB,通過測試。
import sys

result = []
lines = sys.stdin.readlines()
for line in lines:
    if not line.rstrip(): continue
    a, b, n = map(int, line.split())
    int_part = a//b
    dec_part = str(a%b*(10**n) // b).zfill(n)
    result.append(f"{int_part:d}.{dec_part}\n")
sys.stdout.write("".join(result))


2025年7月11日 星期五

ZeroJudge 解題筆記:a528. 大數排序

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


ZeroJudge 題目連結:a528. 大數排序

解題想法


因為 Python 支援大數運算,可以直接排序。C++ 則要用字串格式,分成兩個字串 a、b 開頭都是 +,開頭都是 -,a 正 b 負、a 負 b 正,共 4 種狀況排序。

Python 程式碼


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

for line in sys.stdin:
    if not line.strip(): continue
    n = int(line)
    nums = sorted([int(sys.stdin.readline()) for _ in range(n)])
    result = [f"{num:d}\n" for num in nums]
    sys.stdout.write("".join(result))


2025年7月10日 星期四

ZeroJudge 解題筆記:a524. 手機之謎

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


ZeroJudge 題目連結:a524. 手機之謎

解題想法


這題在 Python 可以用 itertools.permutations,在 C++ 可以用 algorithm 函式庫的 prev_permutation 産生所有的排列,速度很快。也可以用函式遞迴産生所有的排列方式。由於測資有空行,用 Python 解題時要記得跳過空行,否則會吃 RE。

Python 程式碼


寫法1,用 itertools.permutations 産生所有的排列,使用時間約為 0.2 s,記憶體約為 7.5 MB,通過測試。
import sys
from itertools import permutations

result = []
lines = sys.stdin.readlines()
for line in lines:
    if not line.strip(): continue  # 跳過空行
    n = int(line)
    for arr in permutations(range(n, 0, -1), n):  # 從 n ~ 1 的數字取 n 個排列
        res = [f"{a:d}" for a in arr]  # arr 的內容轉成字串存入 res
        result.append("".join(res) + "\n")  # res 接成一個字串加入 result
sys.stdout.write("".join(result))  # 一次輸出所有的結果

寫法2,用函式遞迴窮舉所有的排列,使用時間約為 0.4 s,記憶體約為 7.5 MB,通過測試。
import sys

result = []
def perm(arr, nums, used, depth):
    # 代入已選數字串列 arr, 可用數字串列 nums,已選數字的集合 used,目標長度 depth
    if len(arr) == depth:  # 遞迴出口,如果 arr 長度等於 depth
        result.append("".join([f"{a:d}" for a in arr]) + "\n")  # 接成字串加入 result
        return
    for num in nums:  # 依序由 nums 讀取數字
        if num in used: continue  # 如果 num 已用過,找下一個數字
        arr.append(num)  # num 接到 arr 最後面
        used.add(num)  # num 加入 used
        perm(arr, nums, used, depth)  # 遞迴
        arr.pop()  # 從 arr 最後面移除數字
        used.remove(num)  # 從 used 移除 num

lines = sys.stdin.readlines()
for line in lines:
    if not line.strip(): continue  # 跳過空行
    n = int(line)
    perm([], list(range(n, 0, -1)), set(), n)
sys.stdout.write("".join(result))  # 一次輸出所有的結果


2025年7月9日 星期三

ZeroJudge 解題筆記:a417. 螺旋矩陣

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


ZeroJudge 題目連結:a417. 螺旋矩陣

解題想法


我是先填完矩陣的值再輸出,速度會慢一點。

Python 程式碼


使用時間約為 0.9 s,記憶體約為 21.3 MB,通過測試。
def draw(n, m):
    ### 填格子 ###
    if m == 1:  # 方向順序是右、下、左、上
        dr, dc = (0, 1, 0, -1), (1, 0, -1, 0)
    else:  # 方向順序是下、右、上、左
        dr, dc = (1, 0, -1, 0), (0, 1, 0, -1)
    matrix = [[0]*n for _ in range(n)]  # n*n 矩陣,預設值皆為 0
    r, c, v = 0, 0, 1  # 座標 (r, c),值為 1
    d = 0  # 一開始朝右方或下
    while v < n*n:  # 還沒有填完
        matrix[r][c] = v  # 設定此格的值
        nr, nc = r+dr[d], c+dc[d]  # 要走的格子
        if nr < 0 or nr >= n or nc < 0 or nc >= n or matrix[nr][nc] != 0:  # 出界或已經填過了
            d = (d+1)%4  # 轉向
            nr, nc = r+dr[d], c+dc[d]  # 更新 (nr, nc)
        r, c = nr, nc  # 走到 (nr, nc)
        v += 1  # 值加 1
    matrix[r][c] = v  # 設定最後一格的值
    ### 輸出 ###
    for row in matrix:
        for c, v in enumerate(row):
            print(f"{v:5d}", end="\n" if c == n-1 else "")

T = int(input())
for t in range(T):
    if t > 1: print()
    draw(*map(int, input().split()))


2025年7月8日 星期二

ZeroJudge 解題筆記:a414. 位元運算之進位篇

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


ZeroJudge 題目連結:a414. 位元運算之進位篇

解題想法


假設二進位制數字 $n$ 要加 1,其進位次數等於 $n$ 從最右側向左算共有幾個連續的 1。

Python 程式碼


使用時間約為 0.8 s,記憶體約為 65.9 MB,通過測試。這題的測資量很大,用 print 輸出會很慢,大約要 2 s。
import sys

result = []
lines = sys.stdin.readlines()
for line in lines:
    n = int(line)
    if n == 0: break
    cnt = 0
    while n&1:
        n >>= 1
        cnt += 1
    result.append(f"{cnt:d}\n")
sys.stdout.write("".join(result))


2025年7月7日 星期一

ZeroJudge 解題筆記:a410. 解方程

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


ZeroJudge 題目連結:a410. 解方程

解題想法


這題用克拉瑪公式,假設二元一次方程式為 $$ \left\{\begin{matrix} a_1 x + b_1 y &= c_1 \\ a_2 x + b_2 y &= c_2 \end{matrix}\right. $$ 令 $$ \Delta = \begin{vmatrix} a_1 & b_1 \\ a_2 & b_2 \end{vmatrix} ~~~~~\Delta_x = \begin{vmatrix} c_1 & b_1 \\ c_2 & b_2 \end{vmatrix} ~~~~~\Delta_y = \begin{vmatrix} a_1 & c_1 \\ a_2 & c_2 \end{vmatrix} $$ 有以下三種狀況
  1. 如果 $\Delta \neq 0$,方程式恰有一組解,其解為 $x = \frac{\Delta_x}{\Delta}, ~ y = \frac{\Delta_y}{\Delta}$
  2. 如果 $\Delta = 0$,但 $\Delta_x, \Delta_y$ 至少有一個不為 0,無解。
  3. 如果 $\Delta = \Delta_x = \Delta_y = 0$,無限多組解。


Python 程式碼


使用時間約為 18 ms,記憶體約為 3.3 MB,通過測試。
a1, b1, c1, a2, b2, c2 = map(int, input().split())
delta = a1*b2 - a2*b1
deltax = c1*b2 - c2*b1
deltay = a1*c2 - a2*c1
if delta != 0:  # 恰有一組解
    print(f"x={deltax/delta:.2f}")
    print(f"y={deltay/delta:.2f}")
else:
    if deltax != 0 or deltay != 0: print("No answer")   # 無解
    elif deltax == 0 and deltay == 0: print("Too many") # 無窮多組解


2025年7月6日 星期日

ZeroJudge 解題筆記:a291. nAnB problem

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


ZeroJudge 題目連結:a291. nAnB problem

解題想法


先掃過一次答案與猜測的數字,計算位置、數字都對的數量 A,如果不是位置、數字都對,則用兩個串列記錄 0 ~ 9 各個數字的數量。接下來再掃過這兩個串列,計算位置不對、數字對的數量 B。但是這一題我在 Python 上遇到很大的困難,換了好幾種寫法都超時,用 C++ 會過關但是速度還不夠快,需要改變寫法。

Python 程式碼


超時。
import sys
   
result = []
for line in sys.stdin:
    if not line.rstrip(): continue
    ans = list(map(int, line.split()))
    n = int(sys.stdin.readline())
    for _ in range(n):
        guess = list(map(int, sys.stdin.readline().split()))
        A, B = 0, 0  # 位置、數字都對的數量 A,位置不對、數字對的數量 B
        cnt_a, cnt_g = [0]*10, [0]*10  # 0 ~ 9 各個數字的數量
        for a, g in zip(ans, guess):  # 先掃一輪
            if a == g: A += 1  # 位置、數字都對,A 加 1
            else:  # 反之,記錄數字對應的數量
                cnt_a[a] += 1
                cnt_g[g] += 1
        for a, g in zip(cnt_a, cnt_g):  # 計算 B 的數量
            if a >= g: B += g
            else: B += a
        result.append(f"{A:d}A{B:d}B\n")
sys.stdout.write("".join(result))