AtCoder Beginner Contest 315 題解
All problems solved by python
題意
給定一個字串s,刪除其中的母音(a,e,i,o,u)。
思路
字串操作。
1 2 3 4 5 s = input () target = "aeiou" for ch in s: if ch not in target: print (ch, end="" )
題意
給定 n 個月的日曆,第i個月有 D i D_i D i 天,且保證總天數為奇數,詢問中間的那一天是幾月幾號,即求 ( Σ D i + 1 ) / 2 (\Sigma D_i +1)/2 ( Σ D i + 1 ) /2 對應的月份和日期
思路
找出middle_day
後,從第1個月開始遍歷,將middle_day
扣除本月日數,直到無法相減為止,最後的結果即為日期。
1 2 3 4 5 6 7 8 9 10 11 M = int (input ()) days = list (map (int , input ().split())) mid = (sum (days) + 1 ) // 2 for i in range (0 , M): if mid - days[i] <= 0 : print (i + 1 , mid) break else : mid -= days[i]
題意
給定 N N N 碗冰淇淋,每碗冰淇淋都有其對應的口味 F i F_i F i 和滿意度 S i S_i S i ,我們可以在其中任取兩碗,求最大滿意度。
當 F i ≠ F j F_i \neq F_j F i = F j 時,滿意度為 S i + S j S_i + S_j S i + S j
當 F i = F j F_i = F_j F i = F j 時,滿意度為 S i + S j 2 S_i + \frac{S_j}{2} S i + 2 S j
思路
直接考慮所需的兩種情況,即口味相同和口味不同的情況
對口味相同的情況,maintain一個dict,保存所有相同口味的冰淇淋的滿意度,對不同的口味取出其最大兩項,即為此情況下的滿意度
對口味不同的情況,maintain一個list,保存口味和滿意度的tuple,依照滿意度做排序後,從第2項開始遍歷,直到出現與首項口味不同的項為止,兩項相加即為這種情況的滿意度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 N = int (input ()) flavors = [] dic = {i:[] for i in range (1 , N + 1 ) } for _ in range (N): f, d = map (int , input ().split()) dic[f].append(d) flavors.append((f, d)) flavors.sort(key=lambda x: x[1 ], reverse=True ) ans = 0 for f in range (1 , N + 1 ): if len (dic[f]) >= 2 : dic[f].sort(reverse=True ) ans = max (ans, dic[f][0 ] + dic[f][1 ] // 2 ) for f, d in flavors[1 :]: if f != flavors[0 ][0 ]: ans = max (ans, flavors[0 ][1 ] + d) break print (ans)
個人覺得這題比E、F還難,但也許只是後面兩題的考點比較常見,所以自己比較熟悉而已。
題意
給定一個 H × W 的矩陣,若某行或某列所有元素相同,則可以被刪除,求最後剩餘的元素個數。
思路
所有元素相同即該行或該列只出現一個元素,因此我們maintain在某行或某列中,所有字母出現的對應次數(row/col),以及在某行或某列中,出現幾種不同字母(row_c/col_c)。
接著找出該行或該列只有一種字母的直行橫列,並對該行或該列做刪除,最後剩餘的長寬相乘即為答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 A = 26 row = [[0 ] * A for _ in range (H)] row_c = [0 ] * H col = [[0 ] * A for _ in range (W)] col_c = [0 ] * W num_row, num_col = H, W for i in range (H): for j in range (W): idx = ord (c[i][j]) - ord ('a' ) row[i][idx] += 1 if row[i][idx] == 1 : row_c[i] += 1 col[j][idx] += 1 if col[j][idx] == 1 : col_c[j] += 1 while True : del_row, del_col = [], [] for i in range (H): if row_c[i] == 1 and num_col >= 2 : del_row.append(i) for j in range (W): if col_c[j] == 1 and num_row >= 2 : del_col.append(j) if del_row == [] and del_col == []: break def remove (i, j ): if c[i][j] != ' ' : idx = ord (c[i][j]) - ord ('a' ) row[i][idx] -= 1 if row[i][idx] == 0 : row_c[i] -= 1 col[j][idx] -= 1 if col[j][idx] == 0 : col_c[j] -= 1 c[i][j] = ' ' for i in del_row: for j in range (W): remove(i, j) num_row -= len (del_row) for j in del_col: for i in range (H): remove(i, j) num_col -= len (del_col) print (num_row * num_col)
題意
假設有 N 本書,編號從 1 到 N。如果想要閱讀第 i 本書,則必須先閱讀與其相關的所有書。所求為閱讀第 1 本書之前,需要先閱讀哪些書。
思路:Topological sort
需要注意Python的遞迴深度預設只有1000 1000 1000 ,在test case裡會有幾個Runtime Error,需要設定遞迴深度。
因為所求為閱讀第1本書之前需要閱讀哪些書,所以對第一個節點做DFS即可,輸出時忽略此節點。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import syssys.setrecursionlimit(10 **9 ) N = int (input ()) prerequisities = [list (map (int , input ().split(" " )))[1 :] for _ in range (N)] flags = [0 ] * N ans = [] def dfs (i ): if flags[i] == 2 : return True if flags[i] == 1 : return False flags[i] = 1 for j in prerequisities[i]: if not dfs(j-1 ): return False flags[i] = 2 ans.append(i+1 ) return True dfs(0 ) for idx in ans[:-1 ]: print (idx, end=" " )
題意
在一個坐標系中有若干個檢查點,你需要依序經過這些檢查點,所求為走過的歐幾里得距離總和。你也可以選擇忽略一些檢查點不走,但如果跳過了 k k k 個檢查點,最終的距離將要加上 2 k − 1 2^{k-1} 2 k − 1 作為懲罰,求考慮懲罰後最短的距離。
思路:動態規劃
令 d p [ i ] [ j ] dp[i][j] d p [ i ] [ j ] 表示前i + 1 i+1 i + 1 個點中,跳過j j j 個點的最短路徑
跳過j j j 個點的轉移方程:d p [ i + j + 1 ] [ k ] = m i n ( d p [ i + j + 1 ] [ k ] , d p [ i ] [ k − j ] + d i s t a n c e ) dp[i+j+1][k] = min(dp[i+j+1][k], dp[i][k-j] + distance) d p [ i + j + 1 ] [ k ] = min ( d p [ i + j + 1 ] [ k ] , d p [ i ] [ k − j ] + d i s t an ce )
最後考慮 d p [ N − 1 ] [ k ] dp[N-1][k] d p [ N − 1 ] [ k ] ,對忽略的點數量 k k k 分別計算其懲罰,最後輸出最小值。
因為懲罰為 2 k − 1 2^{k-1} 2 k − 1 ,可以猜測若忽略的點數量過多,則懲罰會大到無法構成最佳解,因此可以設定 k k k 的邊界,此處我設定成30。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import mathN = int (input ()) checkpoints = [list (map (int , input ().split(" " ))) for _ in range (N)] BOUND = min (N, 30 ) dp = [[math.inf] * BOUND for _ in range (N)] dp[0 ][0 ] = 0 for i in range (N): for j in range (BOUND): next_i = i + 1 + j if next_i >= N: break distance = math.dist(checkpoints[i], checkpoints[next_i]) for k in range (j, BOUND): dp[next_i][k] = min (dp[next_i][k], dp[i][k-j] + distance) ans = dp[N-1 ][0 ] for k in range (1 , BOUND): ans = min (ans, dp[N-1 ][k] + 2 **(k-1 )) print (ans)