# Python 必做練習題 Writeup
> [name=curious]
###### tags: `S3 資訊安全 Writeup`
## 上午
### d483: hello, world
> link:https://zerojudge.tw/ShowProblem?problemid=d483
**思路:**
在螢幕上印出 `hello, world`
:::spoiler 解法
```python=
print('hello, world')
```
:::
---
### a001: 哈囉
> link:https://zerojudge.tw/ShowProblem?problemid=a001
**思路:**
將輸入的字串加在 `hello, ` 後面
:::spoiler 解法
```python=
print(f'hello, {input()}')
```
:::
---
### a002: 簡易加法
> link:https://zerojudge.tw/ShowProblem?problemid=a002
**思路:**
將輸入的兩個數字(字串)轉成 `int` 型態,相加後輸出
:::spoiler 解法
```python=
sum = 0
for i in input().split():
sum += int(i)
print(sum)
```
:::
---
### d827: 買鉛筆
> link:https://zerojudge.tw/ShowProblem?problemid=d827
**思路:**
因為一次買一打(12 支)比分成 12 次買還要便宜,所以先算可以買幾打鉛筆,再算剩下多少支鉛筆是單獨購買,最後計算他們總共需要多少錢
:::spoiler 解法
```python=
n = int(input())
print(50*(n//12) + 5*(n%12))
```
:::
---
### d064: ㄑㄧˊ 數?
> link:https://zerojudge.tw/ShowProblem?problemid=d064
**思路:**
奇數除以 2 後都會餘 1,偶數除以 2 都會整除
:::spoiler 解法
```python=
if (int(input()) % 2) == 0:
print('Even')
else:
print('Odd')
```
:::
---
### a058: MOD3
> link:https://zerojudge.tw/ShowProblem?problemid=a058
**思路:**
明顯 `3k` 除以 3 的餘數是 0,`3k + 1` 除以 3 的餘數是 1,`3k + 2` 除以 3 的餘數是 2。先識別每一個數除以 3 的餘數是多少,再統計各個不同餘數的數個有幾個
:::spoiler 解法
```python=
sum = [0,0,0]
for i in range(int(input())):
sum[int(input())%3] += 1
print(' '.join([str(i) for i in sum]))
```
:::
---
### a009: 解碼器
> link:https://zerojudge.tw/ShowProblem?problemid=a009
**思路:**
此題需要使用字元的 ASCII 碼來計算,可以使用 `ord(s)` 來將字元轉成其對應的 ASCII 碼,`chr(n)` 來將 ASCII 碼轉成其對應的字元。先計算範例輸出的 ASCII 碼相對範例輸入的 ASCII 碼位移了多少,然後將輸入的字串變成一串 ASCII 碼,將剛剛計算的位移加上去,最後將那一串 ASCII 碼換成字串。
:::spoiler 解法
```pyton=
cipher = input()
plain = ''
for s in cipher:
plain += chr(ord(s) - 7)
print(plain)
```
:::
---
### a147: Print it all
> link:https://zerojudge.tw/ShowProblem?problemid=a147
**思路:**
將 1~n 的整數都跑過一遍,輸出不是 7 倍數的數字
:::spoiler 解法
```python=
while True:
n = input()
if n == '0':
break
for i in range(1,int(n)):
if (i % 7) == 0:
continue
print(i,end=' ')
print()
```
:::
---
### a149: 乘乘樂
> link:https://zerojudge.tw/ShowProblem?problemid=a149
**思路:**
將每一個數的每一位都分開,然後相乘
:::spoiler 解法
```python=
for i in range(int(input())):
x = 1
for s in input():
x *= int(s)
print(x)
```
:::
---
### a003: 兩光法師占卜術
> link:https://zerojudge.tw/ShowProblem?problemid=a003
**思路:**
按照公式 `S=(M*2+D)%3` 計算出 `S` 的值後,判斷不同 `S` 的值輸出不同的字串
:::spoiler 解法
```python=
M,D = input().split()
S=(int(M)*2+int(D))%3
if S == 0:
print('普通')
elif S == 1:
print('吉')
else:
print('大吉')
```
:::
---
### a005: Eva 的回家作業
> link:https://zerojudge.tw/ShowProblem?problemid=a005
**思路:**
先判斷數列是等差還是等比數列,若是等差數列的話 $a_i + a_{i+2} = 2 \cdot a_{i+1}$,若是等比數列的話 $a_{i} \cdot a_{i+2} = {a_{i+1}}^2$,判斷後就可以裡用以上的公式算出第 5 項
:::spoiler 解法
```python=
t = int(input())
for i in range(t):
seq = [int(i) for i in input().strip().split()]
if seq[2] + seq[0] == 2*seq[1]:
seq.append(2*seq[3] - seq[2])
print(' '.join([str(i) for i in seq]))
else:
seq.append(seq[3]**2//seq[2])
print(' '.join([str(i) for i in seq]))
```
:::
---
### a004: 文文的求婚
> link:https://zerojudge.tw/ShowProblem?problemid=a004
**思路:**
因為需要讀取到 EOF 之後終止,所以可以用
```python
While True:
try:
...
except EOFError:
break
```
做到這樣的效果。讀取輸入後,就可以從條件最嚴格的開始判斷(是否為 400 的倍數 => 是否為 100 的倍數 => 是否為 4 的倍數)是潤年還是平年。
:::spoiler 解法
```python=
while True:
try:
year = int(input())
if year%400 == 0:
print('閏年')
elif year%100 == 0:
print('平年')
elif year%4 == 0:
print('閏年')
else:
print('平年')
except EOFError:
break
```
:::
---
### e189: 3的倍數 - 面試題
> link:https://zerojudge.tw/ShowProblem?problemid=e189
**思路:**
如果一個數字是 3 的倍數,那這個數字除以 3 的餘數就是 0
:::spoiler 解法
```python=
while True:
try:
n = int(input())
if n % 3 == 0:
print('YES')
else:
print('NO')
except EOFError:
break
```
:::
---
### a024: 最大公因數(GCD)
> link:https://zerojudge.tw/ShowProblem?problemid=a024
**思路:**
已知 $\operatorname{gcd}(a,b) = \operatorname{gcd}(b,a\%b)$ 且 $\operatorname{gcd}(a,0) = a$,可以用這兩個公式建構一個求最大公因數的函數(將 `a`,`b` => `b`,`a%b` 然後再繼續運算直到 `a%b == 0`),後續就直接調用這個函數求最大公因數
:::spoiler 解法
```python=
def gcd(a,b):
while True:
a, b = b, a % b
if b == 0:
break
return a
a,b = (int(i) for i in input().strip().split())
print(gcd(a,b))
```
:::
---
### a022: 迴文
> link:https://zerojudge.tw/ShowProblem?problemid=a022
**思路:**
可以建構一個列表是輸入字串的各個字元,將這個列表複製之後反轉,看看兩個列表有沒有相等,相等的話就是迴文
:::spoiler 解法
```python=
seq_reverse = [s for s in input().strip()]
seq = seq_reverse.copy()
seq_reverse.reverse()
if seq_reverse == seq:
print('yes')
else:
print('no')
```
:::
---
### a038: 數字翻轉
> link:https://zerojudge.tw/ShowProblem?problemid=a038
**思路:**
將輸入分解成各個位數的列表後反轉,將反轉的列表重新組成一個數字,將此轉成 `int` 型別,就可以消去多餘的 0 而後輸出
:::spoiler 解法
```python=
seq = [s for s in input().strip()]
seq.reverse()
print(int(''.join(seq)))
```
:::
---
### a148: You Cannot Pass?!
> link:https://zerojudge.tw/ShowProblem?problemid=a148
**思路:**
如果 n 科的分數平均大於 59,那就等價於 n 科的分數相加大於 59*n
:::spoiler 解法
```python=
while True:
try:
seq = [int(i) for i in input().strip().split()]
sum = 0
for s in seq[1:]:
sum += s
if sum > 59*seq[0]:
print('no')
else:
print('yes')
except EOFError:
break
```
:::
---
### a104: 排序
> link:https://zerojudge.tw/ShowProblem?problemid=a104
**思路:**
對一個列表 `s` 排序可以使用他的方法 `s.sort()` 來對他排序,執行完方法 `s` 內的元素順序就已經由小到大排好了
:::spoiler 解法
```python=
while True:
try:
n = int(input())
seq = [int(i) for i in input().strip().split()]
seq.sort()
print(' '.join([str(i) for i in seq]))
except EOFError:
break
```
:::
---
### I-1.2021.11_P1-修補圍籬
> link:https://zerojudge.tw/ShowProblem?problemid=g595
**思路:**
如果 `h[0] == 0` 或 `h[n-1] == 0` 的話,那需要的成本分別要加上 `h[1]` 和 `h[n-2]`,其他的 `h[i] == 0` 的話,需要的成本要加上 `min(h[i-1],h[i+1])`,將 `h` 全部遍歷一遍後就可以得到總共需要的成本
:::spoiler 解法
```python=
n = int(input())
seq = [int(i) for i in input().strip().split()]
sum = 0
for i in range(n):
if (i == 0 and seq[i] == 0):
sum += seq[1]
continue
if (i == n - 1 and seq[i] == 0):
sum += seq[-2]
continue
if (seq[i] == 0 and i != 0 and i != n-1):
sum += min(seq[i-1],seq[i+1])
print(sum)
```
:::
---
### I-2.2022.01_P1-程式交易
> link:https://zerojudge.tw/ShowProblem?problemid=h081
**思路:**
可以設定初始狀況為持有股票和股價為 `price = a[0]`,後續如果持有股票,且現在的股價 `a[i]` 大於等於原本的股價 `price` 加 `D`,那就賣出股票(獲得利潤為 `a[i] - price`)且將股價設成 `price = a[i]`,如果不持有股票,且現在的股價 `a[i]` 小於等於原本的股價 `price` 減 `D`,那就買進股票,且將股價設成 `price = a[i]`。這樣遍歷 `a[0] ~ a[n-1]` 後就可以得到總利潤並輸出
:::spoiler 解法
```python=
n,D = (int(i) for i in input().strip().split())
a = [int(i) for i in input().strip().split()]
sum = 0
have_stonk = True
price = a[0]
for i in range(1,n):
if (have_stonk and a[i] >= (price + D)):
sum += a[i] - price
price = a[i]
have_stonk = False
continue
if ((not have_stonk) and (a[i] <= (price - D))):
price = a[i]
have_stonk = True
print(sum)
```
:::
---
### 2021.09_P1-七言對聯
> link:https://zerojudge.tw/ShowProblem?problemid=g275
**思路:**
可以利用一個布林值代表是否符合規則,一個字串代表違反的規則,從規則 A 開始檢查,如果違反規則就將那個布林值改成 `False` 並將該規則的字符加到那個字串。全部檢查完後,那個布林值就代表有沒有違反規則,那個字串就代表違反了哪一條規則
:::spoiler 解法
```python=
n = int(input())
for i in range(n):
right = True
wrong_code = ''
seq1 = input().strip().split()
seq2 = input().strip().split()
if (seq1[1] == seq1[3]) or (seq2[1] == seq2[3]) or (seq1[1] != seq1[5]) or (seq2[1] != seq2[5]):
right = False
wrong_code += 'A'
if (seq1[6] != '1') or (seq2[6] != '0'):
right = False
wrong_code += 'B'
if (seq1[1] == seq2[1]) or (seq1[3] == seq2[3]) or (seq1[5] == seq2[5]):
right = False
wrong_code += 'C'
if right:
print('None')
else:
print(wrong_code)
```
:::
---
### 2021.01_P1-購買力
> link:https://zerojudge.tw/ShowProblem?problemid=f605
**思路:**
如果一樣商品 3 天內的最大價格減掉最小價格大於 `d`,則會以這個商品三天的平均價格購買此商品
::: spoiler 解法
```python=
n,d = (int(i) for i in input().strip().split())
obj_num = 0
price = 0
for i in range(n):
seq = [int(i) for i in input().strip().split()]
if (max(seq) - min(seq)) >= d:
obj_num += 1
price += sum(seq)//len(seq)
print(obj_num,price)
```
:::
---
### 2020.10_P1-人力分配
> link:https://zerojudge.tw/ShowProblem?problemid=f312
**思路:**
因為題目有說 n 會在 1~100 的範圍內,所以直接窮舉出對於 n 不同 X1 和 X2 可以得到的 Y1 + Y2 的值,然後再取最大值
::: spoiler 解法
```python=
(A1,B1,C1) = (int(i) for i in input().strip().split())
(A2,B2,C2) = (int(i) for i in input().strip().split())
n = int(input())
max_sum = -100000000
for i in range(n+1):
Y_sum = A1*(i**2) + B1*i + C1 + A2*((n-i)**2) + B2*(n-i) + C2
if Y_sum > max_sum:
max_sum = Y_sum
print(max_sum)
```
:::
---
### 2020.07_P1-購物車
> link:https://zerojudge.tw/ShowProblem?problemid=f579
**思路:**
如果有一陣列 `s` 中有元素 `e`,如果要計算 `s` 中 `e` 的數量,可以用 `s.count(e)` 來取得。這一題需要求出一個同時有買 a 和 b 的人,所以那個人的購物車紀錄中 a 和 b 的數量一定要比 -a 和 -b 的數量多,之後遍歷所有人後就可以得到有多少人滿足這個條件
:::spoiler 解法
```python=
a,b = (int(i) for i in input().strip().split())
n = int(input())
num = 0
for i in range(n):
seq = [int(i) for i in input().strip().split()]
if (seq.count(a) > seq.count(-a)) and (seq.count(b) > seq.count(-b)):
num += 1
print(num)
```
:::
---
## 下午
### PPC_Ez:hello, world
> link:https://120.114.62.214/challenges#hello%20world
**思路:**
直接在 terminal 中輸入題目給的指令就結束了
:::spoiler 解法
打開 terminal 輸入:
```bash
$ nc 120.114.62.214 2405
```
等待幾秒後就可以看到 flag 了
flag:`CTF{Hel10WorLD123}`
:::
---
### PPC_Ez:count
> link:https://120.114.62.214/challenges#count
**思路:**
這一題需要在 `b'I say <i> you say?\n'` 後輸出 `<i>\n`
::: spoiler 解法
```python=
from pwn import *
r = remote('120.114.62.214',2403)
for i in range(100):
r.sendlineafter(b'?\n',str(i+1).encode())
r.interactive()
```
flag:`CTF{gOOD4tMatHYOUarE}`
:::
---
### PPC_Ez:3rd
> link:https://120.114.62.214/challenges#3rd
**思路:**
這一題比較多人有問題的是那堆數字到底是幾行?其實從 `numbers : ` 到數字結束只有一行,可以左右拉動 terminal 的邊界,可以發現 `numbers : `後面明顯沒有換行,所以只有一行。然後取得輸入的數字列表後可以用 `seq.sort()` 對 `seq` 由小到大排列,然後再取出第三大的輸出(`seq[-3]`)就可以了
::: spoiler 解法
```python=
from pwn import *
r = remote('120.114.62.214',2400)
r.recvuntil(b'----- Now You Turn -----\n')
seq = [int(i.decode()) for i in r.recvline().strip().split()[2:]]
seq.sort()
r.sendlineafter(b'answer : ',str(seq[-3]).encode())
r.interactive()
```
flag:`CTF{yoUaReInth33RdpL4c3}`
:::
---
### PPC_Ez:beautify
> link:https://120.114.62.214/challenges#beautify
**思路:**
對於一個字串 `s`,可以使用 `s.replace('a','b')` 會返回一個字串且將原字串中的 `'a'` 代換成 `'b'`,使用 `s.lower()` 會返回一個字串且將原字串中所有字母改成小寫。使用上面兩個字串的方法,可以將輸入的字串(記得要用 `s.decode()` 將字串的型別從 `bytes` 改成 `str`)改成一個我們想要的形式
::: spoiler 解法
```python=
from pwn import *
r = remote('120.114.62.214',2401)
r.recvuntil(b'----- Now You Turn -----\n')
sentence = r.recvline().strip().split(b' : ')[1].decode()
sentence = sentence.replace('-',' ').replace('_',' ').lower()
r.sendlineafter(b'answer : ',sentence.encode())
r.interactive()
```
flag:`CTF{NoWYoUKNoWhOWt0STRinG}`
:::
---
### PPC_Ez:calendar
> link:https://120.114.62.214/challenges#calendar
**思路:**
這一題的想法跟 `a004: 文文的求婚` 的想法相同,只是輸出輸入改成用 `pwntools` 而已
::: spoiler 解法
```python=
from pwn import *
r = remote('120.114.62.214',2402)
r.recvuntil(b'----- Example -----\nyear : 2020\nanswer : leap\n')
for i in range(100):
r.recvline()
year = int(r.recvline().strip().split()[2].decode())
if year%400 == 0:
r.sendlineafter(b'answer : ',b'leap')
elif year%100 == 0:
r.sendlineafter(b'answer : ',b'ordinary')
elif year%4 == 0:
r.sendlineafter(b'answer : ',b'leap')
else:
r.sendlineafter(b'answer : ',b'ordinary')
r.interactive()
```
flag:`CTF{2O20HapPY1e4pYE4r!!!}`
:::
---
### PPC_Hard:calculator
> link:https://120.114.62.214/challenges#calculator
**思路:**
這一題可以直接判斷是加、減或乘(看看左邊的那兩個數是加、減或乘等於有邊的數)來回傳運算符
::: spoiler 解法
```python=
from pwn import *
r = remote('120.114.62.214',5119)
r.recvuntil(b'Can you help us?\n')
for i in range(100):
r.recvline()
seq = r.recvline().strip().decode().split()
if (int(seq[0]) + int(seq[2])) == int(seq[4]):
r.sendlineafter(b'? ',b'+')
elif (int(seq[0]) - int(seq[2])) == int(seq[4]):
r.sendlineafter(b'? ',b'-')
else:
r.sendlineafter(b'? ',b'*')
r.interactive()
```
flag:`MyFirstCTF{QLwxhEsyUfKQrYxyEeFe}`
:::
---
### PPC_Hard:lambda
> link:https://120.114.62.214/challenges#lambda
**思路:**
用 `nc` 連上後可以得到這些資訊
```
here is some functions f
f0(x) = 3x^2 + x + 3
f1(x) = 5x^2 + 8
f2(x) = 4x^3 + 6x + 6
f3(x) = 7x^3 + 5x^2
f4(x) = x^2 + 4x + 3
```
所以直接定義函數來表達這些多項式,然後接到輸入的時候就直接用函數計算就可以輸出了
::: spoiler 解法
```python=
from pwn import *
def f(x,n):
if n == 0:
return 3*(x**2) + x + 3
if n == 1:
return 5*(x**2) + 8
if n == 2:
return 4*(x**3) + 6*x + 6
if n == 3:
return 7*(x**3) + 5*(x**2)
return x**2 + 4*x + 3
r = remote('120.114.62.214',5124)
r.recvuntil(b'----- wave : example -----\n')
r.recvlines(3)
for i in range(100):
r.recvline()
n = int(r.recvline().strip().split()[2].decode())
x = int(r.recvline().strip().split()[2].decode())
r.sendline(str(f(x,n)).encode())
r.interactive()
```
flag:`MyFirstCTF{R0gUe on3 - A st4r wARs LamBd4}`
:::
---
### PPC_Hard:temperature
> link:https://120.114.62.214/challenges#temperature
**思路:**
華氏溫標轉攝氏溫標的公式為 $C = \frac{5}{9} \cdot (F - 32)$
如果 `5*(F - 32)` 被 9 整除的話,C 就為 `5*(F-32)//9`,如果不是的話 C 就為 `f'{5*(F-32)}/9'`
::: spoiler 解法
```python=
from pwn import *
r = remote('120.114.62.214',5127)
r.recvlines(5)
for i in range(100):
r.recvline()
tem_f = int(r.recvline().strip().split()[2].decode())
tem_c = (tem_f - 32)*5
if tem_c % 9 == 0:
r.sendlineafter(b'Celsius : ',str(tem_c//9).encode())
else:
r.sendlineafter(b'Celsius : ',str(tem_c).encode() + b'/9')
r.interactive()
```
flag:`MyFirstCTF{h4rRy potTer anD tHe phiL0sOph3r's TeMper4tuRe}`
:::