{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

不可迭代:你不能用 for 去遍歷它

  • 一種資料結構,和 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\) 格,負數代表往左、正數代表往右
  • 如果玩家試圖走出棋盤,那他會撞牆停在原地
  • 走完後,統計現在腳下那格的數字

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

https://zerojudge.tw/ShowProblem?problemid=o712

https://zerojudge.tw/ShowProblem?problemid=r490

https://zerojudge.tw/ShowProblem?problemid=r489

https://zerojudge.tw/ShowProblem?problemid=q837

https://zerojudge.tw/ShowProblem?problemid=q182

你可以改一下西洋棋騎士問題

# CHAPTER 2

def

https://zerojudge.tw/ShowProblem?problemid=o713

https://atcoder.jp/contests/abc383/tasks/abc383_c(這是淹水問題但我覺得很簡單)

# CHAPTER 2

set

# 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
  • 拿 for 做迭代但不用教 iterator,EOF
  • 你還沒有教 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)
Made with Slides.com