1"""
2์ค์ ๋ฌธ์ ํ์ด (Practice Problems)
3Comprehensive Practice Problems
4
5๋ค์ํ ์๊ณ ๋ฆฌ์ฆ์ ์กฐํฉํ์ฌ ํด๊ฒฐํ๋ ์ข
ํฉ ๋ฌธ์ ๋ค์
๋๋ค.
6"""
7
8from typing import List, Tuple, Dict, Set, Optional
9from collections import defaultdict, deque, Counter
10from heapq import heappush, heappop
11from functools import lru_cache
12from bisect import bisect_left, bisect_right
13import sys
14
15
16# =============================================================================
17# 1. ํฌ ํฌ์ธํฐ + ์ด๋ถํ์: ๋ถ๋ถ ๋ฐฐ์ด ํฉ
18# =============================================================================
19
20def subarray_sum_k(arr: List[int], k: int) -> int:
21 """
22 ํฉ์ด k์ธ ๋ถ๋ถ ๋ฐฐ์ด์ ๊ฐ์
23 ์๊ฐ๋ณต์ก๋: O(n)
24 """
25 count = 0
26 prefix_sum = 0
27 sum_count = defaultdict(int)
28 sum_count[0] = 1
29
30 for num in arr:
31 prefix_sum += num
32 # prefix_sum - k๊ฐ ์ด์ ์ ๋์๋ค๋ฉด
33 count += sum_count[prefix_sum - k]
34 sum_count[prefix_sum] += 1
35
36 return count
37
38
39def longest_subarray_sum_at_most_k(arr: List[int], k: int) -> int:
40 """
41 ํฉ์ด k ์ดํ์ธ ๊ฐ์ฅ ๊ธด ๋ถ๋ถ ๋ฐฐ์ด
42 (์์๋ง ์๋ ๊ฒฝ์ฐ)
43 ์๊ฐ๋ณต์ก๋: O(n)
44 """
45 n = len(arr)
46 max_len = 0
47 current_sum = 0
48 left = 0
49
50 for right in range(n):
51 current_sum += arr[right]
52
53 while current_sum > k and left <= right:
54 current_sum -= arr[left]
55 left += 1
56
57 max_len = max(max_len, right - left + 1)
58
59 return max_len
60
61
62# =============================================================================
63# 2. BFS + DP: ์ต๋จ ๊ฒฝ๋ก + ๋น์ฉ
64# =============================================================================
65
66def shortest_path_with_fuel(n: int, edges: List[Tuple[int, int, int]],
67 start: int, end: int, fuel: int) -> int:
68 """
69 ์ฐ๋ฃ ์ ํ์ด ์๋ ์ต๋จ ๊ฒฝ๋ก
70 edges: (u, v, fuel_cost)
71 ๋ฐํ: ์ต์ ๊ฑฐ๋ฆฌ (๋ถ๊ฐ๋ฅํ๋ฉด -1)
72 ์๊ฐ๋ณต์ก๋: O(V * F + E)
73 """
74 graph = defaultdict(list)
75 for u, v, cost in edges:
76 graph[u].append((v, cost))
77 graph[v].append((u, cost))
78
79 # BFS with state: (node, remaining_fuel)
80 INF = float('inf')
81 dist = [[INF] * (fuel + 1) for _ in range(n)]
82 dist[start][fuel] = 0
83
84 queue = deque([(start, fuel, 0)]) # (node, remaining_fuel, distance)
85
86 while queue:
87 node, remaining, d = queue.popleft()
88
89 if node == end:
90 return d
91
92 if d > dist[node][remaining]:
93 continue
94
95 for neighbor, cost in graph[node]:
96 if remaining >= cost:
97 new_fuel = remaining - cost
98 if d + 1 < dist[neighbor][new_fuel]:
99 dist[neighbor][new_fuel] = d + 1
100 queue.append((neighbor, new_fuel, d + 1))
101
102 return -1
103
104
105# =============================================================================
106# 3. ๊ทธ๋ฆฌ๋ + ์ฐ์ ์์ ํ: ์์
์ค์ผ์ค๋ง
107# =============================================================================
108
109def max_profit_scheduling(jobs: List[Tuple[int, int, int]]) -> int:
110 """
111 ์์
์ค์ผ์ค๋ง: ๊ฒน์น์ง ์๊ฒ ์ ํํ์ฌ ์ต๋ ์ด์ต
112 jobs: (์์, ์ข
๋ฃ, ์ด์ต)
113 ์๊ฐ๋ณต์ก๋: O(n log n)
114 """
115 n = len(jobs)
116 if n == 0:
117 return 0
118
119 # ์ข
๋ฃ ์๊ฐ์ผ๋ก ์ ๋ ฌ
120 jobs = sorted(jobs, key=lambda x: x[1])
121 end_times = [job[1] for job in jobs]
122
123 dp = [0] * (n + 1)
124
125 for i in range(n):
126 start, end, profit = jobs[i]
127
128 # ํ์ฌ ์์
์์ ์ ์ ๋๋๋ ๋ง์ง๋ง ์์
129 j = bisect_right(end_times, start) - 1
130
131 # ์ ํ vs ๋น์ ํ
132 dp[i + 1] = max(dp[i], (dp[j + 1] if j >= 0 else 0) + profit)
133
134 return dp[n]
135
136
137# =============================================================================
138# 4. ๋ฌธ์์ด + DP: ํธ์ง ๊ฑฐ๋ฆฌ ์์ฉ
139# =============================================================================
140
141def min_operations_to_palindrome(s: str) -> int:
142 """
143 ๋ฌธ์์ด์ ํฐ๋ฆฐ๋๋กฌ์ผ๋ก ๋ง๋๋ ์ต์ ์ฝ์
/์ญ์
144 = ๊ธธ์ด - LCS(s, reverse(s))
145 """
146 n = len(s)
147 t = s[::-1]
148
149 # LCS
150 dp = [[0] * (n + 1) for _ in range(n + 1)]
151 for i in range(1, n + 1):
152 for j in range(1, n + 1):
153 if s[i - 1] == t[j - 1]:
154 dp[i][j] = dp[i - 1][j - 1] + 1
155 else:
156 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
157
158 return n - dp[n][n]
159
160
161def longest_palindromic_subsequence(s: str) -> int:
162 """
163 ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ ๋ถ๋ถ ์์ด
164 = LCS(s, reverse(s))
165 """
166 n = len(s)
167 t = s[::-1]
168
169 dp = [[0] * (n + 1) for _ in range(n + 1)]
170 for i in range(1, n + 1):
171 for j in range(1, n + 1):
172 if s[i - 1] == t[j - 1]:
173 dp[i][j] = dp[i - 1][j - 1] + 1
174 else:
175 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
176
177 return dp[n][n]
178
179
180# =============================================================================
181# 5. ๊ทธ๋ํ + Union-Find: ์ฐ๊ฒฐ ์์
182# =============================================================================
183
184class DSU:
185 """Disjoint Set Union"""
186 def __init__(self, n: int):
187 self.parent = list(range(n))
188 self.rank = [0] * n
189 self.size = [1] * n
190
191 def find(self, x: int) -> int:
192 if self.parent[x] != x:
193 self.parent[x] = self.find(self.parent[x])
194 return self.parent[x]
195
196 def union(self, x: int, y: int) -> bool:
197 px, py = self.find(x), self.find(y)
198 if px == py:
199 return False
200 if self.rank[px] < self.rank[py]:
201 px, py = py, px
202 self.parent[py] = px
203 self.size[px] += self.size[py]
204 if self.rank[px] == self.rank[py]:
205 self.rank[px] += 1
206 return True
207
208
209def min_cost_to_connect_all(n: int, edges: List[Tuple[int, int, int]]) -> int:
210 """
211 ๋ชจ๋ ๋
ธ๋๋ฅผ ์ฐ๊ฒฐํ๋ ์ต์ ๋น์ฉ (MST)
212 edges: (u, v, cost)
213 """
214 edges = sorted(edges, key=lambda x: x[2])
215 dsu = DSU(n)
216 total_cost = 0
217 edges_used = 0
218
219 for u, v, cost in edges:
220 if dsu.union(u, v):
221 total_cost += cost
222 edges_used += 1
223 if edges_used == n - 1:
224 break
225
226 return total_cost if edges_used == n - 1 else -1
227
228
229def count_islands(grid: List[List[int]]) -> int:
230 """
231 ์ฌ์ ๊ฐ์ (Union-Find ๋ฒ์ )
232 grid[i][j] = 1: ๋
, 0: ๋ฌผ
233 """
234 if not grid or not grid[0]:
235 return 0
236
237 rows, cols = len(grid), len(grid[0])
238 dsu = DSU(rows * cols)
239 count = 0
240
241 def idx(r, c):
242 return r * cols + c
243
244 for r in range(rows):
245 for c in range(cols):
246 if grid[r][c] == 1:
247 count += 1
248
249 directions = [(0, 1), (1, 0)]
250 for r in range(rows):
251 for c in range(cols):
252 if grid[r][c] == 0:
253 continue
254 for dr, dc in directions:
255 nr, nc = r + dr, c + dc
256 if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
257 if dsu.union(idx(r, c), idx(nr, nc)):
258 count -= 1
259
260 return count
261
262
263# =============================================================================
264# 6. ์ธ๊ทธ๋จผํธ ํธ๋ฆฌ + ์ขํ ์์ถ: ๊ตฌ๊ฐ ์ฟผ๋ฆฌ
265# =============================================================================
266
267class SegmentTree:
268 """๊ตฌ๊ฐ ํฉ ์ธ๊ทธ๋จผํธ ํธ๋ฆฌ"""
269 def __init__(self, n: int):
270 self.n = n
271 self.tree = [0] * (4 * n)
272
273 def update(self, node: int, start: int, end: int, idx: int, delta: int):
274 if idx < start or idx > end:
275 return
276 if start == end:
277 self.tree[node] += delta
278 return
279 mid = (start + end) // 2
280 self.update(2 * node, start, mid, idx, delta)
281 self.update(2 * node + 1, mid + 1, end, idx, delta)
282 self.tree[node] = self.tree[2 * node] + self.tree[2 * node + 1]
283
284 def query(self, node: int, start: int, end: int, l: int, r: int) -> int:
285 if r < start or end < l:
286 return 0
287 if l <= start and end <= r:
288 return self.tree[node]
289 mid = (start + end) // 2
290 return (self.query(2 * node, start, mid, l, r) +
291 self.query(2 * node + 1, mid + 1, end, l, r))
292
293
294def count_smaller_after_self(nums: List[int]) -> List[int]:
295 """
296 ๊ฐ ์์ ๋ค์ ์๋ ๋ ์์ ์์์ ๊ฐ์
297 ์ธ๊ทธ๋จผํธ ํธ๋ฆฌ + ์ขํ ์์ถ
298 ์๊ฐ๋ณต์ก๋: O(n log n)
299 """
300 # ์ขํ ์์ถ
301 sorted_nums = sorted(set(nums))
302 rank = {v: i for i, v in enumerate(sorted_nums)}
303 n = len(sorted_nums)
304
305 result = []
306 st = SegmentTree(n)
307
308 # ์ญ์์ผ๋ก ์ฒ๋ฆฌ
309 for num in reversed(nums):
310 r = rank[num]
311 # r๋ณด๋ค ์์ ์ธ๋ฑ์ค์ ํฉ (= ๋ ์์ ์์์ ๊ฐ์)
312 count = st.query(1, 0, n - 1, 0, r - 1) if r > 0 else 0
313 result.append(count)
314 st.update(1, 0, n - 1, r, 1)
315
316 return result[::-1]
317
318
319# =============================================================================
320# 7. ๋ฐฑํธ๋ํน: ์กฐํฉ ์ต์ ํ
321# =============================================================================
322
323def solve_sudoku(board: List[List[str]]) -> bool:
324 """
325 ์ค๋์ฟ ํด๊ฒฐ
326 board: 9x9, '1'-'9' ๋๋ '.'
327 """
328
329 def is_valid(row: int, col: int, num: str) -> bool:
330 # ํ ๊ฒ์ฌ
331 if num in board[row]:
332 return False
333 # ์ด ๊ฒ์ฌ
334 for r in range(9):
335 if board[r][col] == num:
336 return False
337 # 3x3 ๋ฐ์ค ๊ฒ์ฌ
338 box_row, box_col = 3 * (row // 3), 3 * (col // 3)
339 for r in range(box_row, box_row + 3):
340 for c in range(box_col, box_col + 3):
341 if board[r][c] == num:
342 return False
343 return True
344
345 def solve() -> bool:
346 for row in range(9):
347 for col in range(9):
348 if board[row][col] == '.':
349 for num in '123456789':
350 if is_valid(row, col, num):
351 board[row][col] = num
352 if solve():
353 return True
354 board[row][col] = '.'
355 return False
356 return True
357
358 return solve()
359
360
361# =============================================================================
362# 8. ๋นํธ๋ง์คํฌ DP: ์ธํ์ ๋ฌธ์ (TSP)
363# =============================================================================
364
365def tsp(dist: List[List[int]]) -> int:
366 """
367 ์ธํ์ ๋ฌธ์ (TSP)
368 ๋ชจ๋ ๋์๋ฅผ ๋ฐฉ๋ฌธํ๊ณ ์์์ ์ผ๋ก ๋์์ค๋ ์ต์ ๋น์ฉ
369 ์๊ฐ๋ณต์ก๋: O(nยฒ * 2^n)
370 """
371 n = len(dist)
372 INF = float('inf')
373
374 @lru_cache(maxsize=None)
375 def dp(mask: int, last: int) -> int:
376 if mask == (1 << n) - 1: # ๋ชจ๋ ๋์ ๋ฐฉ๋ฌธ
377 return dist[last][0] if dist[last][0] > 0 else INF
378
379 result = INF
380 for next_city in range(n):
381 if mask & (1 << next_city):
382 continue
383 if dist[last][next_city] == 0:
384 continue
385 result = min(result, dist[last][next_city] + dp(mask | (1 << next_city), next_city))
386
387 return result
388
389 return dp(1, 0) # 0๋ฒ ๋์์์ ์์
390
391
392# =============================================================================
393# 9. ์ด๋ถํ์ + ๊ทธ๋ฆฌ๋: ํ๋ผ๋ฉํธ๋ฆญ ์์น
394# =============================================================================
395
396def min_max_distance(houses: List[int], k: int) -> int:
397 """
398 k๊ฐ์ ์ฐ์ฒดํต์ ์ค์นํ์ฌ ๊ฐ์ฅ ๋จผ ์ง๊น์ง์ ๊ฑฐ๋ฆฌ ์ต์ํ
399 ํ๋ผ๋ฉํธ๋ฆญ ์์น: "์ต๋ ๊ฑฐ๋ฆฌ๊ฐ d ์ดํ๊ฐ ๋๊ฒ ํ ์ ์๋๊ฐ?"
400 ์๊ฐ๋ณต์ก๋: O(n log D)
401 """
402 houses = sorted(houses)
403 n = len(houses)
404
405 def can_cover(max_dist: int) -> bool:
406 """์ต๋ ๊ฑฐ๋ฆฌ max_dist๋ก ๋ชจ๋ ์ง์ ์ปค๋ฒ ๊ฐ๋ฅํ์ง"""
407 count = 1
408 last_post = houses[0]
409
410 for house in houses:
411 if house - last_post > 2 * max_dist:
412 count += 1
413 last_post = house
414
415 return count <= k
416
417 lo, hi = 0, houses[-1] - houses[0]
418
419 while lo < hi:
420 mid = (lo + hi) // 2
421 if can_cover(mid):
422 hi = mid
423 else:
424 lo = mid + 1
425
426 return lo
427
428
429def min_pages_per_student(pages: List[int], students: int) -> int:
430 """
431 ์ฑ
์ ํ์๋ค์๊ฒ ๋๋ ์ค ๋ ์ต๋ ํ์ด์ง ์ ์ต์ํ
432 (์ฐ์๋ ์ฑ
๋ง ๊ฐ๋ฅ)
433 """
434 def is_feasible(max_pages: int) -> bool:
435 count = 1
436 current = 0
437 for p in pages:
438 if p > max_pages:
439 return False
440 if current + p > max_pages:
441 count += 1
442 current = p
443 else:
444 current += p
445 return count <= students
446
447 lo, hi = max(pages), sum(pages)
448
449 while lo < hi:
450 mid = (lo + hi) // 2
451 if is_feasible(mid):
452 hi = mid
453 else:
454 lo = mid + 1
455
456 return lo
457
458
459# =============================================================================
460# 10. ์ข
ํฉ: LIS + ์ด๋ถํ์
461# =============================================================================
462
463def longest_increasing_subsequence(nums: List[int]) -> Tuple[int, List[int]]:
464 """
465 ๊ฐ์ฅ ๊ธด ์ฆ๊ฐํ๋ ๋ถ๋ถ ์์ด (๊ธธ์ด + ์ค์ ์์ด)
466 ์๊ฐ๋ณต์ก๋: O(n log n)
467 """
468 n = len(nums)
469 if n == 0:
470 return 0, []
471
472 # tails[i] = ๊ธธ์ด i+1 LIS์ ๋ง์ง๋ง ์์ ์ค ์ต์๊ฐ
473 tails = []
474 # ๊ฐ ์์น์์์ LIS ๊ธธ์ด
475 lengths = [0] * n
476 # ์ด์ ์์ ์ถ์
477 prev = [-1] * n
478 # tails์ ์ธ๋ฑ์ค๊ฐ ์ด๋ค ์๋ ์ธ๋ฑ์ค์์ ์๋์ง
479 tail_idx = []
480
481 for i, num in enumerate(nums):
482 pos = bisect_left(tails, num)
483 if pos == len(tails):
484 tails.append(num)
485 tail_idx.append(i)
486 else:
487 tails[pos] = num
488 tail_idx[pos] = i
489
490 lengths[i] = pos + 1
491 if pos > 0:
492 # ์ด์ ์์: lengths[j] == pos์ธ ๋ง์ง๋ง j
493 for j in range(i - 1, -1, -1):
494 if lengths[j] == pos and nums[j] < num:
495 prev[i] = j
496 break
497
498 # LIS ๋ณต์
499 max_len = max(lengths)
500 result = []
501 idx = lengths.index(max_len)
502
503 # ์ต์ ์์์ ์ฐพ๊ธฐ
504 for i in range(n - 1, -1, -1):
505 if lengths[i] == max_len:
506 idx = i
507 break
508
509 while idx != -1:
510 result.append(nums[idx])
511 # ์ด์ ์์ ์ฐพ๊ธฐ
512 target_len = lengths[idx] - 1
513 if target_len == 0:
514 break
515 for j in range(idx - 1, -1, -1):
516 if lengths[j] == target_len and nums[j] < nums[idx]:
517 idx = j
518 break
519 else:
520 break
521
522 return max_len, result[::-1]
523
524
525# =============================================================================
526# ํ
์คํธ
527# =============================================================================
528
529def main():
530 print("=" * 60)
531 print("์ค์ ๋ฌธ์ ํ์ด (Practice Problems) ์์ ")
532 print("=" * 60)
533
534 # 1. ๋ถ๋ถ ๋ฐฐ์ด ํฉ
535 print("\n[1] ๋ถ๋ถ ๋ฐฐ์ด ํฉ")
536 arr = [1, 2, 3, -3, 1, 2]
537 k = 3
538 count = subarray_sum_k(arr, k)
539 print(f" ๋ฐฐ์ด: {arr}, k={k}")
540 print(f" ํฉ์ด {k}์ธ ๋ถ๋ถ ๋ฐฐ์ด ๊ฐ์: {count}")
541
542 # 2. ์์
์ค์ผ์ค๋ง
543 print("\n[2] ์์
์ค์ผ์ค๋ง (์ต๋ ์ด์ต)")
544 jobs = [(1, 3, 50), (2, 5, 20), (4, 6, 70), (6, 7, 60)]
545 profit = max_profit_scheduling(jobs)
546 print(f" ์์
(์์, ์ข
๋ฃ, ์ด์ต): {jobs}")
547 print(f" ์ต๋ ์ด์ต: {profit}")
548
549 # 3. ํฐ๋ฆฐ๋๋กฌ
550 print("\n[3] ๋ฌธ์์ด โ ํฐ๋ฆฐ๋๋กฌ")
551 s = "aebcbda"
552 ops = min_operations_to_palindrome(s)
553 lps = longest_palindromic_subsequence(s)
554 print(f" ๋ฌธ์์ด: '{s}'")
555 print(f" ํฐ๋ฆฐ๋๋กฌ ๋ง๋๋ ์ต์ ์ฐ์ฐ: {ops}")
556 print(f" ๊ฐ์ฅ ๊ธด ํฐ๋ฆฐ๋๋กฌ ๋ถ๋ถ์์ด ๊ธธ์ด: {lps}")
557
558 # 4. MST
559 print("\n[4] ์ต์ ์ ์ฅ ํธ๋ฆฌ")
560 edges = [(0, 1, 4), (0, 2, 3), (1, 2, 1), (1, 3, 2), (2, 3, 4)]
561 cost = min_cost_to_connect_all(4, edges)
562 print(f" ๊ฐ์ : {edges}")
563 print(f" MST ๋น์ฉ: {cost}")
564
565 # 5. ์ฌ์ ๊ฐ์
566 print("\n[5] ์ฌ์ ๊ฐ์")
567 grid = [
568 [1, 1, 0, 0, 0],
569 [1, 1, 0, 0, 0],
570 [0, 0, 1, 0, 0],
571 [0, 0, 0, 1, 1]
572 ]
573 islands = count_islands([row[:] for row in grid])
574 print(f" ๊ทธ๋ฆฌ๋:")
575 for row in grid:
576 print(f" {row}")
577 print(f" ์ฌ์ ๊ฐ์: {islands}")
578
579 # 6. ๋ค์ ์๋ ๋ ์์ ์์
580 print("\n[6] ๊ฐ ์์ ๋ค์ ๋ ์์ ์์ ๊ฐ์")
581 nums = [5, 2, 6, 1]
582 result = count_smaller_after_self(nums)
583 print(f" ๋ฐฐ์ด: {nums}")
584 print(f" ๊ฒฐ๊ณผ: {result}")
585
586 # 7. TSP
587 print("\n[7] ์ธํ์ ๋ฌธ์ (TSP)")
588 dist = [
589 [0, 10, 15, 20],
590 [10, 0, 35, 25],
591 [15, 35, 0, 30],
592 [20, 25, 30, 0]
593 ]
594 min_cost = tsp(dist)
595 print(f" ๊ฑฐ๋ฆฌ ํ๋ ฌ:")
596 for row in dist:
597 print(f" {row}")
598 print(f" ์ต์ ๋น์ฉ: {min_cost}")
599
600 # 8. ํ๋ผ๋ฉํธ๋ฆญ ์์น
601 print("\n[8] ์ฑ
๋ฐฐ๋ถ ๋ฌธ์ ")
602 pages = [12, 34, 67, 90]
603 students = 2
604 min_pages = min_pages_per_student(pages, students)
605 print(f" ํ์ด์ง: {pages}, ํ์: {students}")
606 print(f" ์ต๋ ํ์ด์ง ์ต์ํ: {min_pages}")
607
608 # 9. LIS
609 print("\n[9] ์ต์ฅ ์ฆ๊ฐ ๋ถ๋ถ ์์ด (LIS)")
610 nums = [10, 9, 2, 5, 3, 7, 101, 18]
611 length, seq = longest_increasing_subsequence(nums)
612 print(f" ๋ฐฐ์ด: {nums}")
613 print(f" LIS ๊ธธ์ด: {length}")
614 print(f" LIS ์์: {seq}")
615
616 # 10. ์๊ณ ๋ฆฌ์ฆ ์ ํ ๊ฐ์ด๋
617 print("\n[10] ์๊ณ ๋ฆฌ์ฆ ์ ํ ๊ฐ์ด๋")
618 print(" | ๋ฌธ์ ์ ํ | ํต์ฌ ์๊ณ ๋ฆฌ์ฆ |")
619 print(" |------------------------|----------------------------|")
620 print(" | ๋ถ๋ถ ๋ฐฐ์ด ํฉ | ํด์๋งต + ํ๋ฆฌํฝ์ค ํฉ |")
621 print(" | ๊ตฌ๊ฐ ์ค์ผ์ค๋ง | ์ ๋ ฌ + ๊ทธ๋ฆฌ๋/DP |")
622 print(" | ๋ฌธ์์ด ๋ณํ | DP (LCS, ํธ์ง๊ฑฐ๋ฆฌ) |")
623 print(" | ๊ทธ๋ํ ์ฐ๊ฒฐ | Union-Find, BFS/DFS |")
624 print(" | ๊ตฌ๊ฐ ์ฟผ๋ฆฌ | ์ธ๊ทธ๋จผํธ ํธ๋ฆฌ, BIT |")
625 print(" | ์กฐํฉ ์ต์ ํ | ๋ฐฑํธ๋ํน + ๊ฐ์ง์น๊ธฐ |")
626 print(" | ์์ ํ์ (์์ n) | ๋นํธ๋ง์คํฌ DP |")
627 print(" | ์ต์๊ฐ ์ต๋ํ | ํ๋ผ๋ฉํธ๋ฆญ ์์น |")
628
629 print("\n" + "=" * 60)
630
631
632if __name__ == "__main__":
633 main()