{Python 語法}

Speed Run 那種,拿來給競程ㄉ

喔對,我左上角的 chapter 都亂寫

# CHAPTER 2

Basic Trivia

就是把我懶得分類的東西

都丟到這個子標題下

# PRESENTING CODE

開發環境

  • 如果你想要生活品質,那上面四個都不推薦
  • 不太好用,但基本上 APCS 考場扣掉 vim 只有這些:
  • LeafPad:一個乾淨的 Text Editor
  • IDLE:記得創建檔案,不要在 shell 跑程式
# PRESENTING CODE

IDLE

Light Attract Bugs

  • 反正我討厭互動模式
  • 按 F5 可以執行或上面有個 Run
# PRESENTING CODE

在終端執行 python

  • linux(APCS考場):搜尋 Terminal,或 ctrl + alt + T
  • 有些 Text Edtior,會直接在下面給你終端,例如 VS Code
  • 避免你剛好用到一個純文字編輯器,沒有一鍵執行
  • windows:搜尋命令提示字元,或搜尋 cmd
# PRESENTING CODE

在終端執行 python

  • 移動工作路徑:
cd {directory}
  • 工作路徑:

你要切到要執行程式的那層資料夾,電腦才找得到檔案

  • 執行 .py 檔案:
python {filename}
# PRESENTING CODE

在終端執行 python

# PRESENTING CODE

Online Judge

讓你解題的地方,丟扣上去,他會告訴你結果

  • 其他還有 CodeForces、資訊之芽的 Sprout Judge 等

你也可以來 kankoo 看每一場比賽的題目難度

灰色是純語法、咖啡色是麻煩實作或資結

綠色是基礎演算法

# PRESENTING CODE

新制 APCS

滿級更容易,天擇不掉人,台清交可能要漲分數了,我是不理解
  • 現在觀念題可以選 C 或 Python,看起來 Python 變很水
  • 可以選你要報哪級,每個級別都有三題,會卡級分上限
# CHAPTER 2

I/O、變數、運算

python 的 i/o 和自動偵測變數很強 ...

甚至有點太強了

所以亂寫會壞掉

print("Hellooooooooooooooooooooooo World")
# 就,對,字面上的意思
# Python 的 print() 會自動換行
# 你可以在程式碼前面加米字號,變整行註解

"""
或是像這樣,用三個引號包起來
可以形成多行註解
註解在 Debug 時很好用
"""
# PRESENTING CODE

Output

print("Hellooooooooooooooooooooooo World", end = "")
# 所以你給他一個新的 end,取代掉默認值就好
# 這樣輸出的結果不會換行,後面不加空白
# PRESENTING CODE

Output

  • 如果你不想換行,或希望輸出後增加其他東西?
  • end 預設為 "\n"
# PRESENTING CODE

變數

變數類型 說明 例子 備註
int 整數 -1、7、36 向下取整
float 浮點數 0.654、-1.98 精度問題
boolean 是或否 True、False 首字母大寫
string 字串 "duck"、'cat' 單或雙引號皆可
  • 基本上 Python 沒有 char 的概念
  • 你可以用 變數類型() 強制轉換變數型別
print(int(-2/3)) # 0
# PRESENTING CODE

變數

  • Python 會自動偵測變數類型,直接宣告就好
_int = 13
_str = "duck"
_float = 3.1415926
_bool = True
_undefined = None # 這意義不大

print(type(_int)) # <class 'int'>
print(type(_str)) # <class 'str'>
print(type(_float)) # <class 'float'>
print(type(_bool)) # <class 'bool'>
print(type(_undefined)) # <class 'NoneType'>

_undefined = 7
print(type(_undefined)) # <class 'int'>
  • 你可以用 type({變數名稱}) 來檢查它的類型
# PRESENTING CODE

變數

  • 請一開始就給變數初始值,你 Debug 時也比較舒服

舉例而言,我很喜歡給 -1e9 這個值

因為一般運算時不可能達到這個數

當你 Debug 看到 -1e9

就知道這個變數從頭到尾都沒被改過

  • type() 可以幹嘛?解決你的 TypeError
# PRESENTING CODE

運算

運算子 說明 例子 備註
+ 3+7 =10
- 16-9 =7
* 3*7 =21
/ 2/3 =0.666...
** 次方 2**7 =128
% 8%3 =1
== 比較 (3+17) == (21-1) True
# PRESENTING CODE

運算

f 是一個 >0 的 float

寫一套程式幫我把 f 無條件捨去至小數點後第三位

做完之後輸出

不要用 round(),如果你知道那是什麼

f = 3.1415926
fixed = int(f * 1000)
print(fixed / 1000)

如果你是想擷取 f 的小數部分呢?

# PRESENTING CODE

運算

print(max(1, 3, 5, 9.14561, 16)) # 16
print(min(1, 3, 5, 9.14561, 16)) # 1
  • max() 和 min():老實說他們跑很慢,但基本上不管
# PRESENTING CODE

bool 運算

print(True and False) # False
print(True or False) # True
print(not True) # False
  • and、or、not
  • De Morgon's Law:

not (A and B) = (not A) or (not B)

# PRESENTING CODE

str 運算

str1 = "abc"
str2 = "efg"

print(str1 + str2) # abcefg
print(str1 * 3) # abcabcabc
  • 你可以用 {變數名}[n] 來提取 str 的第 n 個字母
str1 = "abc"
str2 = "efg"

print(str1[1]) # b
print(str1[2] + str2[0]) # ce
# PRESENTING CODE

str 運算

  • 因為 python 的記憶題儲存機制,你不能更改 str 中的字元
s = "adc"
s[1] = "b"
# TypeError: 'str' object does not support item assignment
  • 解法:
s = "abZdefg"
ss = s[0:2] + "c" + s[3:7]
print(ss)

你也可以直接用 list 裝 char,我喜歡這麼做

# PRESENTING CODE

str 運算

  • 你可以用 ord(一個字母) 來提取字母的 ASCII
ch = "a"
print(ord(ch)) # 97
print(ord(ch)+1) # 98

假設今天 ch 是任何一個小寫字母

寫一套程式幫我判斷

ch 是字母表中的第幾個字母

不要查 ASCII 表,你也不需要背

# PRESENTING CODE

str 運算

假設今天 ch 是任何一個小寫字母

寫一套程式幫我判斷

ch 是字母表中的第幾個字母

不要查 ASCII 表,你也不需要背

ch = "h"
print(ord(ch) - ord('a') + 1)
# PRESENTING CODE

str 分割

str = "A B C D E F G"
a, b, c, d, e, f, g = str.split()
print(d) # D
sen = "I like the night. Without the dark, we’d never see the stars"
a, b = sen.split(". ") # 注意那個空格
print(a)
print(b)
# PRESENTING CODE

Formatted String

pi = 3.14159
r = 3
print(f"r = {r}, perimeter = {2*r*pi}, area = {r*r*pi}")
  • 在你的引號前面加 f
  • 用大括號包住你的變數名稱或運算
  • 其實 Python 還有其他方法做到類似事情但我覺得那些方法可讀性遠低於這個
content = input()
print(f"Your input = {content}")

# 輸入一個字串,再輸出同樣的東西
# PRESENTING CODE

Input

  • input() 吃進來的所有東西都視為 string
  • input() 吃進來的所有東西都視為 string,一次吃一行
  • 用 int() 之類的來轉換型別
T = 1
print(T) # 1
print(bool(T)) # True
# PRESENTING CODE

Input

  • map(function, list):

把 list 的東西,一個一個抓過來代入 function

a, b = map(int, input().split())
print(a+b)
# 堆,你寫出了一個加法器
  • map 其實還有很多用途,但之後再慢慢講
  • 注意要接好 map 傳回來的東西,否則 map 回傳的 type 很怪
# PRESENTING CODE

華氏 / 攝氏溫度轉換

若華氏溫度為 F,一個整數

則可經由公式 \(C = (F-32)\div1.8\)

得出攝氏溫度 C

設計一套程式,吃進一個整數 F,輸出 C

C 以向零取整的方式,轉換至整數

F = int(input())
C = (F-32)/1.8
print(int(C))
# PRESENTING CODE

給你一個整數 N

把它取絕對值後輸出

  • abs({數字}) 是 python 內建的一個方法回傳數字的絕對值
# PRESENTING CODE

有個阿罵每秒走 V 公尺

她每走 A 秒就要休息 B 秒才會繼續走

求她 X 秒可以走多少公尺

喔對,雖然有 if-else 或 loop 可能會比較好做

但這題有完全不需要的辦法

頂多你會需要用到一個 min()

# CHAPTER 2

條件

if (condition_1):
	...
elif (condition_2):
	...
else:
	...
# PRESENTING CODE

條件控制

  • Python 認縮排來判斷範圍!記得加 tab 之類的
  • condition 一定要是一個 bool
if (condition_1):
  	...
	if (condition_1_1):
    	...
    else:
    	...
elif (condition_2):
	...
else:
	...
# PRESENTING CODE

巢狀

  • 再說一次,記得好好縮排
  • 非常不建議在 Python 寫成單行形式,你 debug 會很痛苦
# PRESENTING CODE

根據原始分數轉換等第

吃進一個正整數 k

代表學生的百分制分數

輸出它的等第計分

假設使用者給的分數區間

不屬於以上任何一個分數

則輸出 ?

# PRESENTING CODE

根據原始分數轉換等第

X = int(input())
result = "?"

if (X > 100):
	pass
elif (X <= 100 and X >= 90):
	result = "A+"
elif (X >= 85):
	result = "A"
elif (X >= 80):
	result = "A-"
elif (X >= 77):
	result = "B+"
elif (X >= 73):
	result = "B"
elif (X >= 67):
	result = "C+"
elif (X >= 63):
	result = "C"
elif (X >= 60):
	result = "C-"
elif (X >= 50):
	result = "D"
elif (X > 0):
	result = "F"

print(result)
# PRESENTING CODE
2016 APCS 10 月 - pA

吃進三個正整數 a、b、c 代表三角形三邊長

把他們由小到大輸出

再告訴我這個三角形是直角、鈍角、銳角

或根本不存在

  • 三角形成立條件:任兩邊長相加 > 第三邊
  • 畢氏定理:\(a^2 + b^2 = c^2\)
# PRESENTING CODE
  • 首先,如果你想把 a 和 b 的值互換,python 支援此語法:
a = 1
b = 2
a, b = b, a
# 先說,並非每個語言都支持你這麼寫
  • 那我們可以先保證 \(a \leq b \leq c\) 對吧?
a, b, c = map(int, input().split())

if (a > b):
  a, b = b, a
if (b > c):
  b, c = c, b
if (a > b):
  a, b = b, a
# PRESENTING CODE
  • 為什麼要比較三次?

3

2

1

a, b, c = map(int, input().split())

2

3

1

if (a > b):
  a, b = b, a

2

1

3

if (b > c):
  b, c = c, b

1

2

3

if (a > b):
  a, b = b, a
  • 假設有不只 3 個數字,例如 N 個數字好了,總共要比較幾次?你能找到一個通式嗎?( 實際上這東西叫 Bubble Sort )
# PRESENTING CODE
a, b, c = map(int, input().split())

if (a > b):
  a, b = b, a
if (b > c):
  b, c = c, b
if (a > b):
  a, b = b, a

print(f"{a} {b} {c}")

if (a + b <= c):
    print("No")
elif (a*a + b*b < c*c):
    print("Obtuse")
elif (a*a + b*b == c*c):
    print("Right")
else:
    print("Acute")
# PRESENTING CODE

給你兩個數字 a、b,以及 a 與 b 邏輯運算的結果

求 a 與 b 可能用到 AND、OR、XOR 的哪些運算

誒酥魚羹記得來修

這邊連結爛掉了

# PRESENTING CODE
# CHAPTER 2

迴圈

whille (condition):
  ...
# PRESENTING CODE

whille

  • Python 認縮排來判斷範圍!記得加 tab 之類的
  • condition 一定要是一個 bool
  • 如果 condition 是對的,就一路執行,走到底範圍的底時,會回到範圍最上面
whille (True):
  ...
# PRESENTING CODE

whille

  • 無窮迴圈:
  • 它會導致你迴圈外的東西永遠跳不出來debug 時可以在迴圈的第一行輸出一個提示你就知道迴圈效果是否是自己要的
# PRESENTING CODE

whille

  • break:直接跳出正在跑的這個迴圈
  • continue:剩下的部分別跑了,重新回到迴圈第一行
while (True):
    # 這裡每次迴圈都會被執行到
    if (condition_1):
        continue # 回去第二行
    # 如果 condition_1,那這行就不會跑
    if (condition_2):
        break # 過去第 10 行
    # 如果 (condition_1 or condition_2),這行就不會跑

# 這個不在迴圈中
# PRESENTING CODE

whille

  • 這個會輸出什麼?
n = 0

while (n < 100):
    n += 1
    if (n%2 == 0):
        print(n)
n = 0

while (True):
    n += 1
    if (n%2 == 1):
        continue
    print(n)
    if (n>=100):
        break
 
  
  • 這個呢?
# PRESENTING CODE

range

  • range(l, r, d) 會產生一個等差數列,首項 l 公差 d任何一項都 < r,講帥一點叫左閉右開區間 [l, r)
print(f"{list(range(1, 10, 1))}") # [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(f"{list(range(1, 10, 2))}") # [1, 3, 5, 7, 9]
print(f"{list(range(10, 1, -1))}") # [10, 9, 8, 7, 6, 5, 4, 3, 2]
print(f"{list(range(3, 18, 4))}") # [3, 7, 11, 15]
  • 它的 type 也很奇怪。如果你真的想看裡面長什麼樣子 ( 基本上不會用),要強制轉成 list
print(type(range(1, 10, 1))) # <class 'range'>
# PRESENTING CODE

for

  • for A in B:

A 是隨便一個變數名稱,B 是一個可迭代的物件

(不可迭代物件還是能用,但順序會亂跳)

  • 像在分解一個大物件。B 是一個很大的東西,你拿迴圈去遍歷 B,把 B 的零件一個一個拆下來。每次你拆下來的小零件都叫 A,你可以在迴圈內使用
  • B 有多少零件,for 迴圈就會跑幾次,例如這樣會印出 1 到 10
for i in range(1, 11, 1):
	print(i)
# PRESENTING CODE

for

  • 更多例子:
for i in range(10, 0, -1):
    print(i)
for i in [1, 3, 5, 7, 9]:
    print(i)
n = 0
for i in range(1, 11, 1):
    n += i
print(n)
  • 實例上,for 比 while 常用很多很多,競程通常搭配 range() 控制迴圈次數,例如讀取輸入。或拿來遍歷 string、list、dict、set 之類的

噢,理論上

現在 APCS 的第一題你都能解了

它的範圍真的就考到迴圈而已

# PRESENTING CODE
2017 APCS 3 月 - pA

給你一個正整數 X

找出 X 的 |奇數位數字和 - 偶數位數字和|

  • 一個有點繞的想法:

我每次對 X 做 %10,就可以取出末位數字

我每次對 X 做 // 10,就可以踢掉末位數字

最後 X 會變成 0

# PRESENTING CODE
2017 APCS 3 月 - pA
X = int(input())

now = True
# 如果 now 就加進 odd,否則加進 even
odd, even = 0, 0
while (X>0):
    if (now):
        odd += X%10
    else:
        even += X%10
    X = X // 10
    now = not now

print(abs(odd-even))
  • 我們不能直接跟 python 說我要 int 的第幾位數
  • 但我們可以說,我要 string 的第幾個字元
# PRESENTING CODE
2017 APCS 3 月 - pA
X = input()

odd, even = 0, 0
for i in range(0, len(X), 2):
    odd += ord(X[i]) - ord('0')
for i in range(1, len(X), 2):
    even += ord(X[i]) - ord('0')
print(abs(odd-even))
  • 你可以用 ord(X[i]) - ord('0') 得到這個字元實際上是多少
  • 你可以用 len(X) 來獲取 X 這個 string 的長度
# CHAPTER 2

list - 1

# PRESENTING CODE

list

  • 動態陣列,類似 C++ 的 vector,如果你知道那是什麼
  • 總之就是,你把一坨東西排成一排後綁起來並給每個元素一個唯一的編號
元素 element 5 7 "s" True 'a' "pa" 31 3.1 [2]
編號 index 0 1 2 3 4 5 6 7 8
類型 type int int str bool str str int float list
  • 一種資料結構,aka"拿來放很多變數的容器"
# PRESENTING CODE

list

元素 element 5 7 "s" True 'a' "pa" 31 3.1 [2]
編號 index 0 1 2 3 4 5 6 7 8
類型 type int int str bool str str int float list
  • 編號一定是從零開始 ( 0-based ),幾乎每個語言都這樣
  • list 裡面可以放任何東西,int、float、bool、str ...

甚至另一個 list,或之後教到的 set、dict、class

# PRESENTING CODE

list

  • 宣告:使用中括號或 list()
# 直接宣告一個有起始值的 list
L = [1, 2, 3, 4, 5]
# 宣告一個空的 list
M = []
# 把一坨東西強制轉成 list
N = list(map(int, input().split()))
  • 其實第六行你之前寫題目可能就用過了
# PRESENTING CODE

list

  • 取值:{list 的名稱}[index],把編號裝在中括號裡
L = [1, 2, 3]
print(L[1]) # 2
L = [5, 7, "s", True, "a", "pa", 31, 3.1, [2]]
print(L[2])
print(L[7])
print(L[8]) # [2]
print(L[9]) # IndexError: list index out of range
  • 如果你的取值範圍大於 list 的長度就會爛掉
# PRESENTING CODE

list

  • 歐對,你的 index 也可以是負數,那就是倒著數回來
L = [1, 2, 3]
print(L[0]) # 1
print(L[-1]) # 3
print(L[-2]) # 2
print(L[-3]) # 1
# PRESENTING CODE

基本運算

L = [1, 2, 3]
M = [4, 5, 6]
print(L+M) # [1, 2, 3, 4, 5, 6]
print(L*3) # [1, 2, 3, 1, 2, 3, 1, 2, 3]
  • 加是連接、乘是複製,跟 str 相同
  • 可以用 len({list 的名稱}) 取得 list 長度
L = [1, 2, 3]
print(len(L)) # 3
# PRESENTING CODE

初始化

L = [0] * 100 # 就複製 100 次
  • 假設你想要一個長度 100、每項都是 0 的陣列?
  • 你希望他第 n 項的值是 n+1,\(\forall\,0 \leq n \leq 99\)?
L = [0] * 100 # 你還是要先設定大小
for i in range(1, 101, 1):
  L[i-1] = i # 然後乖乖跑迴圈
# PRESENTING CODE

初始化

L = [0] * 100 # 就複製 100 次
  • 在絕大多數的場合,你都要先設定大小,我們默認每項都是 0

當然如果你想用 -1、None、"empty" 之類的也沒差

  • 每題題目都一定會告訴你測資範圍,這就是在暗示你,list 需要開多大 ( 進階題目中,也是在暗示你時間複雜度,但我們目前不用管時間,你只要寫得出來都一定會過 )
# PRESENTING CODE

增加元素、踢除元素、插入元素

L = [1, 2, 3]
  • 增加:.append({元素}),在 list 的最後面多加一個東西
L.append("s")
print(L) # [1, 2, 3, "s"]
# PRESENTING CODE

增加元素、踢除元素、插入元素

L = [1, 2, 3]
L.pop(1)
print(L) # [1, 3]
print(len(L)) # 2
print(L[2]) # IndexError: list index out of range
  • 踢除:.pop({index}),把 index 的元素踢掉注意 list 長度會改變
# PRESENTING CODE

增加元素、踢除元素、插入元素

L = [1, 2, 3]
L.insert(1, "duck")
print(L) # [1, 2, 'duck', 3]
  • 插入:.insert({index}, {element}),把元素插入到 index 的後面
  • 如果你給的 index \(\geq\) len(L),會被視為 .append()

但沒事不要這麼做,壞習慣:(

# PRESENTING CODE

遍歷

L = [0] * 100
for i in range(1, 101, 1):
  L[i-1] = i
for it in L:
  print(it)
  • 你可以 ( 而且我喜歡 ) 拿 for 迴圈去跑
# PRESENTING CODE

遍歷

  • 如果你把 {list 名稱}[index] 改成 {list 名稱}[L:R]

可以創造出一個包含 [L, R) 所有元素的 list

L = [0] * 100
for i in range(1, 101, 1):
  L[i-1] = i

M_1 = [L[i] for i in range(0, 21, 1)]
M_2 = L[0:21]
# M_1 和 M_2 是完全相同的東西
# [1, 2, 3, 4, 5, ..., 20]
# PRESENTING CODE

插播:in-line 的 for 迴圈

numbers = [1, 2, 3, 4, 5]
  • 「我要什麼 for 來自於誰 in 去哪裡找 」
# 翻譯:我要 "n的平方",對於 "numbers" 裡的每一個 "n"
result = [n ** 2 for n in numbers]
result = []                # 1. 先拿空碗
for n in numbers:          # 2. 一個個拿
    result.append(n ** 2)  # 3. 運算 + 放進去
# PRESENTING CODE

插播:in-line 的 for 迴圈

numbers = [1, 2, 3, 4, 5]
  • 你甚至可以在"去哪裡找"的地方多加一層 if
# 翻譯:我要 "n平方",對於 numbers 裡的 n,但 "只要偶數"
result = [n ** 2 for n in numbers if n % 2 == 0]
result = []
for n in numbers:
    if n % 2 == 0:        # 多了這層過濾
        result.append(n ** 2)
# PRESENTING CODE

回來看購物車

給你 a, b,代表要觀察的商品代號

總共有 \(N\) 名顧客,接下來 \(N\) 行中

每行會有若干個整數 k,\(1 \leq |k| \leq 100\)

如果 k>0,代表顧客拿了一個代號 k 的物品放進購物車

如果 k<0,代表顧客從購物車中移除一個代號 k 的物品

如果 k=0,代表此行結束,顧客去結帳了

求有多少顧客結帳時,購物車中同時有 a 與 b?

# PRESENTING CODE

回來看購物車

  • 為什麼要告訴你 \(|k| \leq 100\)?

代表你陣列開 100 格就夠了

cart = [0] * 117 # 通常會故意開大一點以防萬一
  • 我們假設 cart 的 index 就是商品代號,而 cart[index] 的值是購物車中目前該商品的數量

很多題目都是類似邏輯,我們會讓 index 代表某種東西,value 代表另一種東西,而 list 負責幫我們儲存這種"一對一的關係"

# PRESENTING CODE

回來看購物車

  • 然後呢?然後題目叫你幹嘛你就幹嘛,接著就過了
a, b = map(int, input().split())
N = int(input())
ans = 0

for _ in range(N):
	line = list(map(int, input().split()))
	cart = [0] * 117
	for it in line:
		if (it == 0):
			break
		if (it > 0):
			cart[it] += 1
		else:
			cart[-it] -= 1

	if (cart[a] > 0 and cart[b] > 0):
		ans += 1

print(ans)
# PRESENTING CODE

回來看購物車

  • 其實這題可以不用存整個購物車,你只要管 a 和 b 有幾個,其他數字直接 pass 掉就好
  • 你可能會想用 in 的語法:用於檢驗一個元素是否在 list 中
print(2 in con) # True
judge = False
for it in con:
  if (it == 2):
    judge = True
print(judge) # True
  • 如果今天不只要觀察 a 與 b,而是我直接給你一個 list 叫 con,要你觀察 con 中的所有數字呢?
con = [1, 2, 3, 4, 5]
# PRESENTING CODE

回來看購物車

  • 如果今天不只要觀察 a 與 b,而是我直接給你一個 list 叫 con,要你觀察 con 中的所有數字呢?
con = list(map(int, input().split()))
N = int(input())
ans = 0

for _ in range(N):
	line = map(int, input().split())
	cart = [0] * 117
	for it in line:
		if (it == 0):
			break
		if (it > 0):
			cart[it] += 1
		else:
			cart[-it] -= 1

	judge = True
    for it in con:
      if (cart[it] <= 0):
        judge = False
        braek
    if (judge) ans += 1

print(ans)
# PRESENTING CODE
# PRESENTING CODE
  • 題目默認 0 是費氏數列的第 1 項,其實他沒講清楚

這就是為什麼要先看範測再寫

  • 題目根本沒有給 m 的範圍

一般競技程式或 APCS 的題目

記憶體限制都能允許你把 list 開到 1e6 左右

你可以直接盲猜 ( 老實說再更大,時間上也算不出來 )

# PRESENTING CODE
# PRESENTING CODE
m = int(input())
arr = [0] * (m+17)
arr[0] = 0
arr[1] = 1

for i in range(2, m+1, 1):
	arr[i] = arr[i-1] + arr[i-2]

print(arr[m-1])
  • 當然,你也可以先吃進 m,再決定 list 開多大
# PRESENTING CODE

n 很小,你可以一直掃沒關係

這題跟 list 半點關係都沒有,只是我剛好看到

n 很小,你可以一直掃沒關係

他讀取輸入有點小麻煩

建議用 list(map(int, input().split())) 再代 for 迴圈

# CHAPTER 2

tuple

我本來以為這章會很短

但不知不覺做太多了

# PRESENTING CODE

tuple

tu_1 = (1, 2)
tu_2 = (3, 4, 5, 6)
tu_3 = (7, "st", 1.34, True)
a, b = 8, 9
tu_4 = tuple(a, b)
  • 宣告:用小括號,或 tuple()
  • 一種資料結構,和 list 非常像,但不可迭代、hashable

資料結構:大容器,裡面裝很多變數

Hashable:可以拿來 sort(我等等會講)

# PRESENTING CODE

tuple

  • 一種資料結構,和 list 非常像,但 hashable

資料結構:大容器,裡面裝很多變數或資料結構

Hashable:可以拿來 sort(我下頁會講)

  • 你可以拿 {tuple}[index] 來取值,和 list 很像
tu = (1, 2)
print(tu[0]) #1
print(tu[1]) #2
print(tu[2]) # IndexError: tuple index out of range
# PRESENTING CODE

tuple

  • tuple 不可以修改,這超級重要,它是唯讀的
  • 還記得有誰是這樣嗎?

string

tu = (1, 2)
tu[0] = 3 # TypeError: 'tuple' object does not support item assignment
s = "abcdefg"
s[0] = A # TypeError: 'str' object does not support item assignment
  • 那如果你就是想改變它的值呢?
# PRESENTING CODE

tuple

  • 那如果你就是想改變它的值呢?

重新宣告一個 tuple 取代原本那一個

tu = (1, 2)
tu = (3, tu[1])
s = "abcdefg"
# 補充一下,如果你不打上界,它就會跑到底
s = 'A' + s[1:]
# 所以 s[1:] 就是從 index=1 跑到最後一個字母
  • 話說,string 也是同樣處理方式
# PRESENTING CODE

tuple

  • 運算:一如往常,+ 是連接,* 是複製
a = (1, 2)
b = (3, 4)
print(a+b) # (1, 2, 3, 4)
print(a*2) # (1, 2, 1, 2)
  • 比較:從第 0 個元素開始比,如果相同就再比下一個
# 因為 index=0 時,3 > 1
print((3, 4) > (1, 2)) # True
# 因為 index=0 時,3 = 3,接著看 index=1
# index=1 時,4 > 2
print((3, 4) > (3, 2)) # True
# PRESENTING CODE

Hash Function ( 放心這 APCS 不會考 )

  • 一種很複雜、數學構建而成的函式,具有下列性質

不可逆:你可以用 x 算出 f(x),但不能用 f(x) 推出 x

唯一:如果 f(x) = k,那絕對找不到一個 y 使得 f(y) = k

高敏感度:哪怕 x 和 y 只差一點點,f(x) 和 f(y) 也差超多

  • 在資安界非常重要,舉例而言可以儲存密碼

網站方不可能儲存你密碼的原文嘛,這太蠢了

它只需要在你輸入密碼時,驗證"你有沒有打對"

那它只要檢查 f(x) 即可,就算 f(x) 外洩,駭客也找不到 x

# PRESENTING CODE

進階資料結構 ( 當故事看看就好 )

  • 在更難的題目中,你會開始在意程式執行的時間。而某些資料結構,例如 list,在 push 和 pop 時特別慢,例如 list。如果你要從長度 N 的 list 刪除一個元素,最多要花上 N 筆計算
  • 因為這個"優化",它會要求裡面的元素都 hashable換言之,要能丟進剛剛講的 Hash Function

有些資料結構

為了讓 push 和 pop 更快

會用一些神奇的東西優化

例如 set 和 dict 用 Hash Table

長右邊這個樣子

# PRESENTING CODE

進階資料結構 ( 當故事看看就好 )

  • 因為這個"優化",它會要求裡面的元素都 hashable換言之,要能丟進剛剛講的 Hash Function
  • 那"唯讀"就很重要了,例如 list,你隨時可以改值就不能丟進 Hash Function 了

可能你丟進去時,list 的樣子是 \(L_1\),得到的結果是 \(h_1\)

後來你在這個 list 中 .append() 一個值

現在多個值的 list 叫 \(L_2\),得到的結果是 \(h_2\)

那"優化過的資料結構"就會壞掉

  • 總之,你可以把"裝著 list 的 tuple"丟進 set但你不能直接把 list 丟進 set ( 之後會再強調 )
# PRESENTING CODE

tuple 主要功用 ( 有些還沒教 )

  • 當成 set 和 dict 的 key
  • 當成 function 回傳值,當你要回傳複數個東西
  • 當成免洗的 data class
  • 儲存那些"本來就應該是一對的東西"

例如二維、三維座標。我之後座標一定會用 tuple 存

你可以慢慢培養何時用 tuple、何時用 list 的語感

   但至少你要先看懂怎麼用,因為太常出現了

# CHAPTER 2

list - 2

sorted

# PRESENTING CODE

sorted()

  • 拿來整理"任何可迭代的資結",雖然目前只學過 list
  • sorted({data_sturcture})
li = [3, 5, 1, 7, 9, 6, 1.8]
# 把一個整理完的 list 賦值給 li
li = sorted(li)
print(li) # [1, 1.8, 3, 5, 6, 7, 9]
# PRESENTING CODE

sorted()

li = [3, 5, 1, 7, 9, 6, 1.8]
# 把一個整理完的 list 賦值給 li
li = sorted(li)
print(li) # [1, 1.8, 3, 5, 6, 7, 9]
  • 預設是由小到大,但如果你想反過來呢?

有個預設是 False 的參數叫 reverse

把它設成 True 就好

li = [3, 5, 1, 7, 9, 6, 1.8]
# 把 reverse 改成 True
li = sorted(li, reverse = True)
print(li) # [9, 7, 6, 5, 3, 1.8, 1]

reverse 預設就是 False,如果你沒有要動可以不特別寫出來

# PRESENTING CODE

給你一堆正整數,然後叫你排序

 

喔對,Python 的 sorted() 底層實作老快了

絕對不用擔心時間問題

 

假設有 N 筆資料

最壞情況下只需要 \(N\,log_{2}N\) 次運算就能排序

一般情況下只會更好

比你手動刻的 Bubble Sort (最大運算量 \(N^2\)) 快很多

# PRESENTING CODE

啊嗯,就照做

N = int(input())
L = list(map(int, input().split()))
L = sorted(L)

for it in L:
	print(L, end = " ")
# PRESENTING CODE

配合 tuple 進行排序

  • 如果今天你要排序的東西變得很奇怪,甚至不是數字呢?

e.g.

日期,先比較年、再比較月、再比較日

月份,電腦看不懂 Jan.、Feb.、Mar. 之類的

  • 你可以用 tuple 客製化出很多比較方式
  • 把東西丟進 tuple 裡,先比較的放前面!

因為 tuple 的比較邏輯就是從 index 小的開始比

e.g

(年份, 月份, 日期)、(數字 1~7, 英文簡寫)

# PRESENTING CODE

給你 N 個二維座標,輸出他們排序後的結果

先比較 x,如果 x 相同再比較 y

N = int(input())
coo = []

for _ in range(N):
	x, y = map(int, input().split())
	# 把 x 和 y 裝進 tuple 後塞進 list
	coo.append((x, y))

coo = sorted(coo)
for pos in coo:
	print(f"{pos[0]} {pos[1]}")
# PRESENTING CODE

給你 N 個字串,輸出最"和諧"的那個字串

和諧的定義:"相異字母數"愈少的字串愈和諧,如果相異

字母數相同,那麼字典序愈小的字串愈和諧

競程領域中常常有這種莫名其妙的詞,不用太在乎

  • 先比較相異字母數,再比較字典序
  • 比較字典序,對 Python 而言就是直接比較兩個 str

(相異字母數, 字串本身)

# PRESENTING CODE
# 紀錄每個字母出現過幾次,因為不知道字串長什麼樣子,所以我開比較大
alphabet = [0]*100
for ch in row_text:
  	# 要有個基準把字母校正成 index,那我就選字母 'A' 的 ASCII
	alphabet[ord(ch) - ord('A')] += 1

# 相異字母數
repeat = 0

for letter in alphabet:
	# 如果這個字母出現的次數大於零,反正就是它有出現過
	if (letter > 0):
		repeat += 1
  • 那我要怎麼知道有幾個相異字母

開個 list 計算每個字母出現幾次如何

# PRESENTING CODE
N = int(input())
L = []
for _ in range(N):
	row_text = input()
	# 紀錄每個字母出現過幾次,因為不知道字串長什麼樣子,所以我開比較大
	alphabet = [0]*100
	for ch in row_text:
		alphabet[ord(ch) - ord('a')] += 1

	# 相異字母數
	repeat = 0

	for letter in alphabet:
		# 如果這個字母出現的次數大於零,反正就是它有出現過
		if (letter > 0):
			repeat += 1

	# 把他們包成 tuple 丟進 list
	L.append((repeat, row_text))

# 現在 list 中最小的就是答案!
L = sorted(L)
# L 中最小的是 L[0],而你要取 L[0] 的第 [1] 項
print(L[0][1])

"""
上面那行和下面等價
smallest = L[0]
print(smallest[1])
"""

喔對,簡報中的程式碼區塊是可以用滑鼠滾輪滑動的

# PRESENTING CODE

題目有照難度排序,但都比例題還難

輸出第二高的山的名字

你可能會想把山的高度和名字

綑在同一個 tuple 裡再排序

# PRESENTING CODE

題目有照難度排序,但都比例題還難

題目有說"讀取到 EOF",代表這是上古時期的題目

據我所知現在 APCS 不會這樣出了

EOF 是 End Of File 的意思,現在題目都會直接告訴你測資數,總共有 T 筆測資之類

那個我有空再補充,可以先貼下面模板拿去用

while True:
	try:
		# 把你的 code 寫在這個無窮迴圈裡,要兩個縮排
		# 第一行我給你了,可以接著寫
		N = int(input())

	except EOFError:
		# 不要刪掉這行
		break
# PRESENTING CODE

你可能會想記錄各元素排序前的 index

那可以考慮直接把它丟在 tuple 的最後一項

反正按照題意,最慢最慢,比較到字典序就好

評分找大的、字典序找小的

你可能會想稍微處理一下評分或字串的其中一個

題目有照難度排序,但都比例題還難

# CHAPTER 2

list - 3

現在要玩一個遊戲,給你一個 \(1 \times N\) 個格子的長條棋盤

每個格子上都有一個整數,可以是負數

玩家一開始在第 1 格,總共有 \(Q\) 筆操作

  • 每次操作時,輸入進一個 -6 ~ 6 的數字 \(k\),代表玩家往左或往右走 \(k\) 格,負數代表往左、正數代表往右
  • 如果玩家試圖走出棋盤,那他會撞牆停在原地
  • 走完後,統計現在腳下那格的數字

求玩家在所有操作中,腳下數字的加總

# PRESENTING CODE
# Sample Input
8 # N
3 # Q
5
-3
-7

# Sample Output
pos 1 2 3 4 5 6 7 8
value 2 3 5 7 11 13 17 19
  • 現在位置:1
  • 目前答案:0

起始位置要在第一格

# Sample Input
8 # N
3 # Q
2 3 5 7 11 13 17 19
5
-3
-7

# Sample Output
20
# PRESENTING CODE
# Sample Input
8 # N
3 # Q
5
-3
-7

# Sample Output
pos 1 2 3 4 5 6 7 8
value 2 3 5 7 11 13 17 19
  • 現在位置:1
  • 目前答案:13

右移五格,位置 1+5=6,答案 0+13=13

# Sample Input
8 # N
3 # Q
2 3 5 7 11 13 17 19
5
-3
-7

# Sample Output
20
# PRESENTING CODE
# Sample Input
8 # N
3 # Q
5
-3
-7

# Sample Output
pos 1 2 3 4 5 6 7 8
value 2 3 5 7 11 13 17 19
  • 現在位置:1
  • 目前答案:18

左移三格,位置 6-3=3,答案 13+5=18

# Sample Input
8 # N
3 # Q
2 3 5 7 11 13 17 19
5
-3
-7

# Sample Output
20
# PRESENTING CODE
# Sample Input
8 # N
3 # Q
5
-3
-7

# Sample Output
pos 1 2 3 4 5 6 7 8
value 2 3 5 7 11 13 17 19
  • 現在位置:1
  • 目前答案:20

左移七格,撞牆留在第一格,答案 18+2=20

# Sample Input
8 # N
3 # Q
2 3 5 7 11 13 17 19
5
-3
-7

# Sample Output
20
# PRESENTING CODE
  • 怎麼存圖?

丟在 list 中,index 就是格子編號,value 是值

  • 撞牆怎麼辦?

那就拿 if-else 去判斷,不要讓它走出邊界

N = int(input())
Q = int(input())
graph = [0]*(N+17) # 我習慣用第 1~N 格,會多開很多格子
pos, ans = 1, 0 # 現在位置, 答案

line = list(map(int, input().split()))
for i in range(1, N+1, 1):
  graph[i] = line[i-1]

for _ in range(Q):
  delta = int(input())
  pos += delta
  if (pos < 1):
    pos = 1
  if (pos > N):
    pos = N

  ans += graph[pos]

print(ans)
  
# PRESENTING CODE
  • 如果我們改成 \(N \times N\) 的棋盤呢

還真的就是開 N 個陣列存

  • 但你總不可能手動創建 N 個 list 吧

資料結構是種容器,裡面可以放變數,或更多資料結構

# PRESENTING CODE
# 這邊創造一個 3*3 的二維陣列

graph = []
for _ in range(3):
  tmp = [0]*3 # [0, 0, 0]
  graph.append(tmp)

# [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
print(graph)

資料結構是種容器,裡面可以放變數,或更多資料結構

  • graph 有三個元素,每個元素都是一個 list
  • graph[0]、graph[1]、graph[2] 都是 list,皆為 [0, 0, 0]

graph = [[0, 0, 0],

              [0, 0, 0],

              [0, 0, 0]]

  • graph[0]、graph[1]、graph[2] 都是 list,皆為 [0, 0, 0]
# PRESENTING CODE

二維陣列的 index

  • 取值時要用兩個括號
graph[0][0] = 1
graph[0][1] = 2
graph[0][2] = 3
graph[1][0] = 4
graph[1][1] = 5
graph[1][2] = 6
graph[2][0] = 7
graph[2][1] = 8
graph[2][2] = 9
index = 0 index = 1 index = 2
graph[0] [0][0] [0][1] [0][2]
graph[1] [1][0] [1][1] [1][1]
graph[2] [2][0] [2][1] [2][2]
  • 第一個中括號是告訴 python,你要拿 graph 的哪一個元素第二個中括號是告訴 python,你要拿 graph[i] 的哪個元素
  • 以矩陣的角度來看,graph[i][j] 是指第 i 列的第 j 個元素
# PRESENTING CODE

初始化

  • 嗯對,你可以一列一列 .append 進去,沒人會阻止你
  • 但實務上,我們更傾向用 in-line 的 for 迴圈:

graph = [ [0]*3 for _ in range(3) ]

[0]*3:我每一項元素都要是 [0] 複製 3 次

_:其實我要的永遠都是 [0]*3,前面的內容根本用不到

我要 [0]*3 for 底線和內容無關 in 某個長度是 3 的 list

range(3):這是某個長度是 3 的 list,總共要跑三次

「我要什麼 for 來自於誰 in 去哪裡找 」

result = [n ** 2 for n in range(1, 5)]
# PRESENTING CODE

初始化

graph = [ [0]*3 for _ in range(3) ]

  • 這兩個是完全相同的東西!
graph = [[0]*3 for _ in range(3)]
graph = []
for _ in range(3):
  graph.append([0]*3)
# PRESENTING CODE

拿巢狀 for 遍歷二維陣列

創建一個九九乘法表的表格

graph = [[0]*10 for _ in range(10)]

# 把第 i 列第 j 項設成 i*j
for i in range(1, 10, 1):
  for j in range(1, 10, 1):
    graph[i][j] = i*j

# 其實這樣印會多一個行末空格,但通常沒差
# 如果題目有明言禁止,那再特別處理就好
for i in range(1, 10, 1):
  for j in range(1, 10, 1):
    print(graph[i][j], end = " ")
  # 換行
  print()
# PRESENTING CODE

現在要玩一個遊戲,給你一個 \(N \times N\) 個格子的棋盤

每個格子上都有一個整數,可以是負數

玩家一開始在左上角,總共有 \(Q\) 筆操作

  • 每次操作時,輸入進 U、D、L、R 其中一個大寫英文字母,代表上移、下移、左移、右移一格
  • 如果玩家試圖走出棋盤,那他會撞牆停在原地
  • 走完後,統計現在腳下那格的數字

求玩家在所有操作中,腳下數字的加總

# PRESENTING CODE
# Sample Input
4 # N
3 # Q
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
R
D
D

# Sample Output
18
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

起始位置:(1, 1)

 

pos:(1, 1)

ans = 0

# PRESENTING CODE
# Sample Input
4 # N
3 # Q
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
R
D
D

# Sample Output
18
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

右移一格,答案 +2

 

pos:(1, 2)

ans = 2

# PRESENTING CODE
# Sample Input
4 # N
3 # Q
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
R
D
D

# Sample Output
18
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

下移一格,答案 +6

 

pos:(2, 2)

ans = 8

# PRESENTING CODE
# Sample Input
4 # N
3 # Q
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
R
D
D

# Sample Output
18
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

下移一格,答案 +10

 

pos:(3, 2)

ans = 18

# PRESENTING CODE
  • 圖的部分,就存在二維陣列中。
graph = [[0]*(N+17) for _ in range(N+17)]

for i in range(1, N+1, 1):
  line = list(map(int, input().split()))
  graph[i][1:N+1] = line

存圖的流派很多,選自己喜歡的就好

graph = [[0]*(N+17) for _ in range(N+17)]

for i in range(1, N+1, 1):
  line = list(map(int, input().split()))
  index = 0
  for j in range(1, N+1, 1):
    graph[i][j] = line[index]
    index += 1
graph = [list(map(int, sys.stdin.readline().split())) for _ in range(N)]

一次寫入一行

一次寫入一格

# PRESENTING CODE
  • 移動,嗯就移動,記得處理撞牆就好
x, y = 1, 1
ans = 0
for _ in range(Q):
  opr = input()
  if (opr == 'U'):
    x = max(1, x-1)
  if (opr == 'D'):
    x = min(N, x+1)
  if (opr == 'R'):
    y = max(N, y+1)
  if (opr == 'L'):
    y = min(1, y-1)

  ans += graph[x][y]

往上、往左座標會用扣的

往下、往右座標會用加的

# PRESENTING CODE
N = int(input())
Q = int(input())
graph = [[0]*(N+17) for _ in range(N+17)]

for i in range(1, N+1, 1):
  line = list(map(int, input().split()))
  graph[i][1:N+1] = line

x, y = 1, 1
ans = 0
for _ in range(Q):
  opr = input()
  if (opr == 'U'):
    x = max(1, x-1)
  if (opr == 'D'):
    x = min(N, x+1)
  if (opr == 'R'):
    y = min(N, y+1)
  if (opr == 'L'):
    y = max(1, y-1)

  ans += graph[x][y]
 
print(ans)

全部組起來應該像這樣

# PRESENTING CODE
# PRESENTING CODE

矩陣圖論的題目概念都差不多

但我今天主要放跟移動比較有關的

照做

如果傳送門用過就把它拆了,設成 -1 之類

轉超過一圈的時候要處理一下,或乾脆你拆了整個 list

# PRESENTING CODE

矩陣圖論的題目概念都差不多

但我今天主要放跟移動比較有關的

紀錄一下面向哪裡,用 list 帶過就好

如果你想更快,可以把四個方向存成

[(1, 0), (0, 1), (-1, 0), (0, -1)] 然後拿 index 當方向

# CHAPTER 2

def

# PRESENTING CODE

def

  • 拿來自定義一個 function,例如 print()、max()、min() 都是 Python 幫你寫好的 function
  • 函數是什麼?

INPUT

可以有很多個

OUTPUT

必須是唯一的

# PRESENTING CODE

def

INPUT

可以有很多個

OUTPUT

必須是唯一的

參數 parameters

回傳值 return value

  • 參數可以是 0 個,也可以是很多個
  • 你可以不回傳任何東西,或回傳一個唯一的變數或資結

如果你就是想回傳很多變數呢?

那就把它們包在資結裡面

list、tuple、dict 都很常見

# PRESENTING CODE

def

# 宣告自定義函數
def 函式名稱(參數1, 參數2, 參數3...):
  ...write something...
  return 回傳值
  • 比較兩個數字後回傳:2 個參數,1 個回傳值cstom_ma
def custom_max (a, b):
  if a>b:
    return a
  elif a<b:
    return b
  else:
    return None
bigger = custom_max(-1, 19)
print(bigger) # 19
# 使用自定義函數
函數名稱(參數1, 參數2, 參數3...)
# PRESENTING CODE

def

  • 和使用者打招呼:1 個參數,0 個回傳值
def greeting (name):
  print(f"Hello, {name}")
  
greeting(crocodile) # hello, crocodile
  • 把 list 的第一項、最後一項元素刪除:1 個參數,1 個回傳值
def pop_front_and_back (L):
  if (len(L) <= 1):
    return []
  L.pop(0)
  L.pop(-1)
  return L
  
print(pop_front_and_back([1, 3, 5, 7, 9])) # 3, 5, 7
# PRESENTING CODE

def

def pop_front_and_back (L):
  if (len(L) <= 1):
    return []
  L.pop(0)
  L.pop(-1)
  return L
  
print(pop_front_and_back([1, 3, 5, 7, 9])) # 3, 5, 7
  • 參數名字是你自己取的,隨便你要叫什麼
  • def 只要遇到 return 就會立刻終止

像 while 遇到 break 一樣

所以我三四行之間才不用寫 else

當然你要寫也沒差啦

# PRESENTING CODE

Mutable & Immutable

li = [1, 3, 5, 7, 9]

def pop_front_and_back (L):
  if (len(L) <= 1):
    return []
  L.pop(0)
  L.pop(-1)
  return L
  
print(pop_front_and_back(li)) # [3, 5, 7]
print(li) # [3, 5, 7]
num = 17
def change_to_one(n):
  n = 1
  return n

print(change_to_one(num)) # 1
print(num) # 17

為什麼左邊可以直接修改到 li

但右邊不能直接修改到 num?

# PRESENTING CODE

Mutable & Immutable

  • Mutable:list、set、dict 等等

是可以被改變的、可以被編輯的

如果今天你改了他的值

Python 還是在同一個地址做事

  • Immutable:int、float、bool、str、tuple 等等

唯讀的、不可以被改變的

如果今天你改了他的值 ( 嚴格而言是重新賦值 )

Python 會把你整組地址換掉

對它而言,地址代表的是一個值,而非變數本身

# PRESENTING CODE

Mutable & Immutable

  • Mutable:list、set、dict 等等
  • Immutable:int、float、bool、str、tuple 等等
num = 17
def print_id(n):
  print(id(n)) # 140721529355192
  n = 3
  print(id(n)) # 140721529354744
  
print(id(num)) # 140721529355192
print_id(num) 
  • id(物件) 可以用來取得物件的記憶體位置 ( 不用記,用不到)

140721529355192

這個位置對應 17

140721529354744

這個位置對應 1

li = [1, 3, 5, 7, 9]

def print_id (L):
  print(id(L)) # 2203611419008
  L.pop()
  print(id(L)) # 2203611419008

print(id(li)) # 2203611419008
print_id(li)

list 是 mutable 的

所以無論你怎麼動它

地址都長得一樣

# PRESENTING CODE

Mutable & Immutable

  • Mutable:

對它而言,變數的儲存位置是存"人名"

就算小明從 A 區搬到 B 區 ( 更改變數的值 )

它也不受影響,因為它只記得小明這個人

  • Immutable:

對它而言,變數的儲存位置是存"房子"

如果小明從 A 房子搬到 B 房子 ( 更改變數的值 )

那它的儲存位置也會改變,因為它只記房子不記人名

# PRESENTING CODE

底下這邊會輸出什麼

text = "duck"

def cro (s):
  s = "crocodile"
  return s
  
text = cro(text)
print(text)

crocodile

text = "duck"

def cro (s):
  s = "crocodile"

cro(text)
print(text)

duck

li = [1, 3, 5, 7, 9]

def func (L):
  L += [11, 13, 17, 19]

li = func(li)
print(li)

None ( 因為根本沒有回傳值 )

li = [1, 3, 5, 7]

def func (L):
  L += [11, 13, 17, 19]

func(li)
print(li)

[1, 3, 5 ,7, 11, 13, 17, 19]

# PRESENTING CODE

全域變數與區域變數

def func():
  a = 12
  b = 13
  return a

A = func()
print(A) # 12
print(b) # NameError: name 'b' is not defined
  • 相較其他語言,Python 的變數生命很長,變數在同一個 def 或 class ( 還沒教 ) 內始終有效,不被 for 或 if 等東西切斷。相對,只要走出 def 或 class,在裡面宣告的變數就會無效

你可以輸出 A,因為 A 有被賦值成 func() 的回傳值

你不能輸出 a 或 b,因為他們一走出 def 就無效了、死了

# PRESENTING CODE

全域變數與區域變數

score = 18
def func():
  print(f"The score is {score}")
  
func() # The score is 18
score += 1
func() # The score is 19
score += 1
func() # The score is 20
  • 那如果不屬於任何一個地方 ( 沒有任何縮排 ) 的變數呢?

那就叫全域變數,整個檔案內的人都可以訪問

# PRESENTING CODE

全域變數與區域變數

score = 18
def func():
  score = 20
  print(f"The score is {score}")  # The score is 20
  
func()
print(score)  # 18
  • 如果同一個名字同時被全域變數與區域變數占用那 Python 會以存活範圍最短的區域變數為主

第三個宣告的那個 score,是走不出 def 的

第四行發現全域變數與區域變數都有個人叫 score

以區域變數為主,所以輸出 20

第七行已經讀不到第三行的 score 了,只剩第一行的

# PRESENTING CODE

全域變數與區域變數

score = 18
def func():
  score += 1
  print(f"The score is {score}")
  # UnboundLocalError: cannot access local variable 'score' where it is not associated with a value
func()
  • 但這樣會爛掉
  • 為什麼?因為對 Python 而言,第三行太突兀了

他不知道你是想把全域的 score 加一

還是 score 應該是個區域變數

如果是後者,那你還沒定義它啊?

# PRESENTING CODE

全域變數與區域變數

score = 18
def func():
  global score
  score += 1
  print(f"The score is {score}")

func() # The score is 19
func() # The score is 20
func() # The score is 21
  • 那怎麼做?如果你要修改在區域內修改全域變數必須加上一個關鍵字 global 來宣告

誒我現在這個 score 是指全域的那個喔

宣告同名區域變數前 宣告同名區域變數後
沒加 global 全域變數是唯讀的 以區域變數為主
有加 global 全域變數是可改的 幫全域變數重新賦值
# PRESENTING CODE

全域變數與區域變數

宣告同名區域變數前 宣告同名變數後
沒加 global 全域變數是唯讀的 以區域變數為主
有加 global 全域變數是可改的 幫全域變數重新賦值
score = 18
def func():
  global score
  score = 20
  print(f"The score is {score}")

func()
print(score)

The score is 20
20

score = 18
def func():
  score = 20
  print(f"The score is {score}")

func()
print(score)

The score is 20
18

# PRESENTING CODE

提示字

def is_bigger(x: int, y: int):
  return (x>y)
  • 你可以在參數後面加冒號來附註參數類型
  • 不過上面這些只是註解功能,可以不寫,實際丟進去的東西也可以不同,python 不會幫你檢查
  • 你可以在放參數的小括號後加 -> 來附註回傳值類型
def is_bigger(x: int, y: int) -> bool:
  return (x>y)
# PRESENTING CODE
2025 APCS 10 月 - pA

有一張 R\(\times\)C 的地圖,每個格子都有自己的高度

一開始大家的高度都是 D

在這張地圖中,總共有 K 隻清醒的恐龍

會給你每個恐龍的位置

定義一次"彗星撞擊"如下:

  1. 撞擊範圍是以 (a, b) 為中心,邊長 s 的正方形
  2. 承上,如果判定時有"半格",則視為沒撞到
  3. 範圍內所有清醒的恐龍都會被打暈
  4. 承上,如果撞下去時範圍內已經沒有清醒恐龍,則範圍內每格地面高度減去 d,可以是負數

求撞擊結束後,地圖的最高/最低高度,以及清醒恐龍數

# PRESENTING CODE
  • 先思考我們可以怎麼設計函數,以我的寫法為例:

我要一個 function,吃進兩個座標

代表撞擊區域的左上角與右下角

再吃進一個整數 d,代表撞擊深度

我希望它幫我處理完彗星撞擊的部分

def collide (lx: int, ly: int, rx: int, ry: int, deep: int) -> None:
  global graph # 這是地圖高度
  global dino # 這是恐龍數量
  ...
# PRESENTING CODE
  • 如果你已經有了這些參數,應該怎麼處理撞擊?
  1. 先看有沒有恐龍醒著,有的話敲暈恐龍
  2. 如果都沒有恐龍醒著,那就鑽地板
def collide (lx: int, ly: int, rx: int, ry: int, deep: int) -> None:
  global graph # 這是地圖深度
  global dino # 這是恐龍數輛
  awake = False # 範圍內有沒有恐龍醒著
  for i in range(lx, rx+1, 1):
    for j in range(ly, ry+1, 1):
      if (dino[i][j] > 0): 
        dino[i][j] = 0 # 如果醒著,就敲暈它
        awake = True

  if (awake): # 假設有敲恐龍,就不鑽地板
    return

  for i in range(lx, rx+1, 1):
    for j in range(ly, ry+1, 1):
      graph[i][j] -= deep
# PRESENTING CODE
  • 接下來,該怎麼使用這個函數呢?我們只有正方形的中心

假設正方形的中心在 \((c_x, c_y)\)

而一半的邊長是 diff = s / 2

所以左上角在 \((c_x-\text{diff}, c_y-\text{diff} )\)

右下角在 \((c_x+\text{diff}, c_y+\text{diff} )\)

"只有半格的話不撞",所以 diff 取 s // 2 就好

邊界呢?用 min 和 max 維護

collide(max(0, cx-diff), max(0, cy-diff), min(R-1, cx+diff), min(C-1, cy+diff), d)

注意這題是 0-based

# PRESENTING CODE
  • 吃輸入
R, C, D = map(int, input().split())
graph = [[D]*(C+1) for _ in range(R+1)]
dino = [[0]*(C+1) for _ in range(R+1)]

K = int(input())
for _ in range(K):
  x, y = map(int, input().split())
  dino[x][y] += 1
  • 模擬每一次撞擊
Q = int(input())
for _ in range(Q):
  cx, cy, s, d = map(int, input().split())
  diff = s // 2
  collide(max(0, cx-diff), max(0, cy-diff), min(R-1, cx+diff), min(C-1, cy+diff), d)
# PRESENTING CODE
  • Complete Code
R, C, D = map(int, input().split())
graph = [[D]*(C+1) for _ in range(R+1)]
dino = [[0]*(C+1) for _ in range(R+1)]

def collide (lx: int, ly: int, rx: int, ry: int, deep: int) -> None:
  global graph
  global dino
  awake = False
  for i in range(lx, rx+1, 1):
    for j in range(ly, ry+1, 1):
      if (dino[i][j] > 0):
        dino[i][j] = 0
        awake = True

  if (awake):
    return

  for i in range(lx, rx+1, 1):
    for j in range(ly, ry+1, 1):
      graph[i][j] -= deep


K = int(input())
for _ in range(K):
  x, y = map(int, input().split())
  dino[x][y] += 1

Q = int(input())
for _ in range(Q):
  cx, cy, s, d = map(int, input().split())
  diff = s // 2
  collide(max(0, cx-diff), max(0, cy-diff), min(R-1, cx+diff), min(C-1, cy+diff), d)

heightest, lowest = -100000, 100000
awake_dino = 0
for i in range(0, R, 1):
  for j in range(0, C, 1):
    heightest = max(heightest, graph[i][j])
    lowest = min(lowest, graph[i][j])
    awake_dino += dino[i][j]

print(f"{heightest} {lowest} {awake_dino}")
# PRESENTING CODE

這邊還有兩題

用 def 會好寫很多

如果你很奇怪,確實可以 DFS 或 BFS

但真的不用搞那麼難

我會想把一次"比較"寫成自定義函數

很煩,沒什麼技巧純靠觀察,APCS 就愛出這種躁題

# CHAPTER 2

set

# PRESENTING CODE

離散數學

  • 集合 set:一個容器,裡面的元素是無序且唯一的

{1, 2, 3, 4} = {2, 1, 3, 4},因為集合無序

{1, 1, 1} = {1},因為集合裡的元素唯一

  • 白話而言,你可以想成

集合裡面只記錄"有"或"沒有"

如果你把一個本來就"有"的東西

再次加入集合內,等於什麼也沒發生

# PRESENTING CODE

set

collect_1 = set() #ˊ這當然可以
collect_2 = {None} # 這也可以,會宣告一個裡面有一個元素 None 的 set
collect_3 = {} # 這不行,這會變成 dict
  • 宣告:用 set(),或你有初始值的話也能用大括號
  • 一種資料結構,和 list 非常像,但不可迭代、hashable
# PRESENTING CODE

嘻嘻其實我還沒備這邊

  • ABC_430_B
  • ABC_377_C
# CHAPTER 2

dict

https://atcoder.jp/contests/abc155/tasks/abc155_c

# CHAPTER 2

Recursion

超好笑,這是清大程設上期末考題,果然他們早在 2014 年就喜歡噁心學生

超好笑,這是清大程設上期末考題,果然他們早在 2014 年就喜歡噁心學生

# CHAPTER 2

temp,拿來給我暫放東西用的

  • i/o、變數、if-else、for-while、dict、tuple、sorted、set、list ( 要有二維、要有 deque )、math、recursion、def、class、try-except
  • abs、round、type()、debug、string、and-or-not、min-max
  • 其實好像得教一下 DFS 和 BFS 嗎
  • 你還沒有教 del()、python2 的自定義 cmp
# CHAPTER 2

TODO

  • tuple 可以迭代,你是笨
# CHAPTER 2

拿來蒐集學生問題用的

Part. I

li = [1, 2, 3, 4, 5]

for it in li:
  print(it, end = 'X')
  # 1X2X3X4X5X
 
print('X'.join(li)) # 1X2X3X4X5(然後換行)
# PRESENTING CODE

{str}.join({list})

  • 把 {list} 中的每個元素取出來,中間用 {str} 間隔
  • 和 print(, end = "{str}") 的差別是,join 的"最後"會少一個間隔符號
  • 如果有 judge 叫你用空白分隔元素,且不允許行末空白那可以用 ' '.join({list}) 輸出 ( 否則要用 if-else )
# PRESENTING CODE
N = int(input())
li = list(map(int, input().split()))

ans = 0
tmp = 1 # 目前累計的樓層數
for i in range(1, len(li), 1):
	if (li[i] < li[i-1]):
		# 如果下棟更低,那就繼續飛
		tmp += 1
	else:
		# 別飛了,更新答案
		ans = max(ans, tmp)
		# 看看把現在這棟當起點會不會更好
		tmp = 1

# 如果不加這行,你遇到一路遞減的 case 會爛掉,ans = 0
ans = max(ans, tmp)
print(ans)
# PRESENTING CODE
N, M, Q = map(int, input().split())
graph = [[None]*(M+17) for _ in range(N+17)]
numbers = [None] * (int(10**6) + 17)

for i in range(1, N+1, 1):
  # 加一個 None 只是為了 index 能對上
  line = [None] + list(map(int, input().split()))
  for j in range(1, M+1, 1):
    graph[i][j] = line[j]
    numbers[line[j]] = (i, j)


for _ in range(Q):
  num = int(input())
  tar = numbers[num]
  output = []
  x, y = tar[0], tar[1]

  if (graph[x-1][y] is not None):
    output.append(graph[x-1][y])
  else:
    output.append(graph[N][y])

  if (graph[x+1][y] is not None):
    output.append(graph[x+1][y])
  else:
    output.append(graph[1][y])

  if (graph[x][y-1] is not None):
    output.append(graph[x][y-1])
  else:
    output.append(graph[x][M])

  if (graph[x][y+1] is not None):
    output.append(graph[x][y+1])
  else:
    output.append(graph[x][1])

  output = sorted(output)
  print(f"{output[0]} {output[1]} {output[2]} {output[3]}")

觀察一下測資範圍會發現,其實不用 dict 也能過

# PRESENTING CODE
def rotate(ori: list[list]) -> list[list]:
  R, C = len(ori), len(ori[0])
  ret = [[None]*R for _ in range(C)] # 旋轉過了
  x, y = C-1, 0
  for i in range(0, R, 1):
    for j in range(0, C, 1):
      # print(f"(R, C) = ({R}, {C}), (i, j) = ({i}, {j}), (x, y) = ({x}, {y})")
      ret[x][y] = ori[i][j]
      x -= 1
      if (x < 0):
        x = C-1
        y += 1

  return ret

def flip(ori: list[list]) -> list[list]:
  R, C = len(ori), len(ori[0])
  ret = [[None]*C for _ in range(R)]
  x, y = R-1, 0
  for i in range(0, R, 1):
    for j in range(0, C, 1):
      # print(f"(R, C) = ({R}, {C}), (i, j) = ({i}, {j}), (x, y) = ({x}, {y})")
      ret[x][y] = ori[i][j]
      y += 1
      if (y >= C):
        y = 0
        x -= 1

  return ret

R, C, Q = map(int, input().split())
mat = [[None]*C for _ in range(R)]
for i in range(R):
  line = list(input().split())
  mat[i][0:C] = line

command = list(map(int, input().split()))
command = command[::-1] # 因為要逆推
for it in command:
  if it == 0:
    mat = rotate(mat)
  if it == 1:
    mat = flip(mat)

print(f"{len(mat)} {len(mat[0])}")
for line in mat:
  print(' '.join(line))

別忘記要逆推,然後它就是純粹噁心

# PRESENTING CODE
N, M = map(int, input().split())
maps = [[None]*(M+17) for _ in range(N+17)]
graph = [[0]*(M+17) for _ in range(N+17)]

for i in range(1, N+1, 1):
  line = list(map(int, input().split()))
  maps[i][1:M+1] = line

dire = [(1, 0), (-1, 0), (0, 1), (0, -1)]
for i in range(1, N+1, 1):
  for j in range(1, M+1, 1):
    if (maps[i][j] == 1):
      graph[i][j] += 1
      for it in dire:
        graph[i + it[0]][j + it[1]] += 1
    elif (maps[i][j] == 2):
      for k in range(1, M+1, 1):
        graph[i][k] += 1
      for k in range(1, N+1, 1):
        graph[k][j] += 1
      graph[i][j] -= 1 # 中心會多算到一次

for i in range(1, N+1, 1):
  for j in range(1, M+1, 1):
    print(graph[i][j], end = ' ')
  print()
# PRESENTING CODE
N, M, K, R, C = map(int, input().split())
graph = [[-1]*(M+17) for _ in range(N+17)]
px, py = R+1, C+1 # 現在位置,我把 0-based 校正成 1-based 了
dire = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 右、下、左、上
index = 0 # 代表現在面向哪裡

for i in range(1, N+1, 1): # 吃輸入
	line = list(map(int, input().split()))
	graph[i][1:M+1] = line

def turn(index: int) -> int: # 右轉
	if (index+1 < 4):
		return (index+1)
	return 0

if (graph[px][py] == 0): # 特判:機器人根本不該走路
	print(0)
else:
	score = graph[px][py]
	graph[px][py] -= 1
	gem = 1
	while True:
		# 要轉彎嗎?
		if (score%K == 0):
			index = turn(index)
		nx = px+dire[index][0] # 新座標
		ny = py+dire[index][1] # 新座標
		while (graph[nx][ny] == -1): # 撞牆就轉彎到沒有牆為止
			index = turn(index) 
			nx = px+dire[index][0]
			ny = py+dire[index][1]
		# 走路
		px, py = nx, ny
		# 要停下來嗎?
		if (graph[px][py] == 0):
			break
		# 撿寶石
		score += graph[px][py]
		graph[px][py] -= 1
		gem += 1

print(gem)
  • 主要應該是卡特判跟判斷順序,順序不能錯
# PRESENTING CODE
R, C = map(int, input().split())
graph_1 = [[None]*C for _ in range(R)]
graph_2 = [[None]*C for _ in range(R)]

# 吃輸入
for i in range(0, R, 1):
	line = list(map(int, input().split()))
	graph_1[i][0:C] = line
for i in range(0, R, 1):
	line = list(map(int, input().split()))
	graph_2[i][0:C] = line

# 拿來比較兩張圖,回傳相似度
def cmp(A, B) -> int: # A、B 都是 graph
	if (len(A) != len(B) or len(A[0]) != len(B[0])): # 尺寸不符
		return 0
	S = 0
	for i in range(0, R, 1):
		for j in range(0, C, 1):
			if (A[i][j] == B[i][j]):
				S += 1

	ret = int(S/(R*C) * 100)
	return ret

# 拿來順時針把圖旋轉 90 度,回傳轉完的圖
def turn(G): # parameter 和 return value 都是 graph
	length, width = len(G), len(G[0])
	NG = [[None]*length for _ in range(width)] # 記得反轉長寬
	for i in range(0, length, 1):
		for j in range(0, width, 1):
			# 右到左、上到下,-1 是為了校正回 0-based
			NG[j][length-1-i] = G[i][j]

	return NG

ans = -1
for _ in range(4):
	ans = max(ans, cmp(graph_1, graph_2))
	graph_1 = turn(graph_1)
print(f"{ans}%")
  • 把東西包一包變成 function 會好寫很多

Python 語法 Speed Run

By repkironca

Python 語法 Speed Run

  • 93