arr = [1, 2, 3, 4]best = 0for i in range(len(arr)): for j in range(i + 1, len(arr)): val = arr[i] + arr[j] best = max(best, val)
2) 재귀로 모든 조합 만들기
n = len(arr)choice = []def dfs(idx): if idx == n: # choice 사용 return # 현재 원소 선택 O choice.append(arr[idx]) dfs(idx + 1) choice.pop() # 현재 원소 선택 X dfs(idx + 1)dfs(0)
3) itertools 순열/조합
from itertools import permutations, combinationsfor p in permutations(arr, 3): passfor c in combinations(arr, 2): pass
BFS / DFS
1) 그래프 DFS (재귀)
graph = [[] for _ in range(n + 1)]visited = [False] * (n + 1)def dfs(x): visited[x] = True for nxt in graph[x]: if not visited[nxt]: dfs(nxt)dfs(1)
2) 그래프 BFS (최단 거리)
from collections import dequegraph = [[] for _ in range(n + 1)]dist = [-1] * (n + 1)def bfs(start): q = deque([start]) dist[start] = 0 while q: cur = q.popleft() for nxt in graph[cur]: if dist[nxt] == -1: dist[nxt] = dist[cur] + 1 q.append(nxt)bfs(1)
3) 2차원 격자 DFS/BFS (상하좌우)
dx = [1, -1, 0, 0]dy = [0, 0, 1, -1]def in_range(x, y): return 0 <= x < n and 0 <= y < mdef bfs_grid(sx, sy): from collections import deque q = deque([(sx, sy)]) visited[sx][sy] = True while q: x, y = q.popleft() for d in range(4): nx, ny = x + dx[d], y + dy[d] if in_range(nx, ny) and not visited[nx][ny]: visited[nx][ny] = True q.append((nx, ny))
그래프 최단 거리
1) 다익스트라 (단일 시작점 최단 거리,start에서 각 노드까지의 최단거리 배열)
import heapqINF = 10**15graph = [[] for _ in range(n + 1)] # graph[u] = [(v, w), ...] (u→v, 비용 w)def dijkstra(start): dist = [INF] * (n + 1) # 최대거리로 다 초기화 해놓고 시작 dist[start] = 0 hq = [(0, start)] # (현재까지 비용, 노드) while hq: cost, cur = heapq.heappop(hq) # cost: 최소인 금액, cur: 최소인 노드 if cost > dist[cur]: # 이미 더 싼 경로가 기록됐으면 넘어감 continue for nxt, w in graph[cur]: # 현재 노드 cur에서 갈 수 있는 모든 다음 간선 탐색 nc = cost + w # nxt로 갈 때의 새로운 비용 if nc < dist[nxt]: # nxt로 가는 더 싼 경로를 찾았다면 갱신 dist[nxt] = nc heapq.heappush(hq, (nc, nxt)) # 갱신된 값 push return dist # start에서 각 노드까지 최단 거리
s, a, b 대상으로만 각각 다익스트라 돌려서 합계 최소값 구하는 문제
import heapqINF = 10**15 # "무한대"처럼 사용할 큰 값 (초기 거리값용)def solution(n, s, a, b, fares): """ n : 전체 노드 수 s : 출발 지점 a : A의 도착 지점 b : B의 도착 지점 fares : 간선 정보 [(x, y, 비용), ...] """ answer = INF # graph[u] = [(v, w), (v2, w2), ...] # u → v 비용이 w인 간선들을 저장하는 인접 리스트 graph = [[] for _ in range(n + 1)] # 간선 정보 구성 (무방향 그래프이므로 양쪽에 모두 추가) for x, y, z in fares: graph[x].append((y, z)) # x -> y 비용 z graph[y].append((x, z)) # y -> x 비용 z # print(graph) # 그래프 구조 확인용 # s → 모든 노드 최단거리 s_d = daik(s, n, graph) # a → 모든 노드 최단거리 (무방향이므로 k → a == a → k) a_d = daik(a, n, graph) # b → 모든 노드 최단거리 b_d = daik(b, n, graph) # k를 합승을 끊는 지점(경유지)라고 할 때, # s→k 비용 + k→a 비용 + k→b 비용의 최솟값을 찾는다. for k in range(1, n + 1): if s_d[k] == INF or a_d[k] == INF or b_d[k] == INF: # 도달 불가능한 노드는 건너뜀 continue # 세 경로의 합 중 최소를 정답으로 갱신 answer = min(answer, s_d[k] + a_d[k] + b_d[k]) return answerdef daik(start, n, graph): """ start : 시작 노드 n : 전체 노드 수 graph : 인접 리스트 return : start에서 각 노드까지의 최단거리 배열 """ # dist[i] = start → i 최단거리 dist = [INF] * (n + 1) dist[start] = 0 # 우선순위 큐: (현재까지 비용, 노드) hq = [(0, start)] while hq: cost, cur = heapq.heappop(hq) # 현재 비용이 가장 작은 노드 pop # 이미 더 싼 경로가 기록됐으면 넘어감 if cost > dist[cur]: continue # 현재 노드 cur에서 갈 수 있는 모든 간선 탐색 for (nxt, w) in graph[cur]: # nxt로 갈 때의 새로운 비용 nc = cost + w # nxt로 가는 더 싼 경로를 찾았다면 갱신 if nc < dist[nxt]: dist[nxt] = nc heapq.heappush(hq, (nc, nxt)) # 갱신된 값 push return dist
2) 플로이드-워셜 (모든 쌍 최단 거리)
INF = 10**15dist = [[INF] * (n + 1) for _ in range(n + 1)]# 자기 자신까지 거리는 0for i in range(1, n + 1): dist[i][i] = 0# 간선 정보 입력 (a → b, 비용 c)for a, b, c in edges: dist[a][b] = min(dist[a][b], c)# 플로이드-워셜for k in range(1, n + 1): for i in range(1, n + 1): for j in range(1, n + 1): if dist[i][j] > dist[i][k] + dist[k][j]: dist[i][j] = dist[i][k] + dist[k][j]# dist[i][j] = i에서 j까지의 최단 거리
그리디(Greedy)
1) 정렬 후 순차 선택 (회의실 배정류)
intervals.sort(key=lambda x: x[1]) # 끝나는 시간 기준cnt = 0end_time = 0for s, e in intervals: if s >= end_time: cnt += 1 end_time = e
2) 정렬 + 양 끝에서 선택 (예: 구명보트)
people.sort()i, j = 0, len(people) - 1boat = 0while i <= j: if people[i] + people[j] <= limit: i += 1 j -= 1 boat += 1
3) 동전 거스름 (배수 관계일 때)
coins.sort(reverse=True)cnt = 0for c in coins: use = n // c cnt += use n -= use * c
다이나믹 프로그래밍(DP)
1) 1차원 DP (예: 계단 오르기, 피보나치)
dp = [0] * (n + 1)dp[0] = 0dp[1] = 1for i in range(2, n + 1): dp[i] = dp[i - 1] + dp[i - 2]
2) 2차원 DP (예: 정수 삼각형)
dp = [[0] * len(row) for row in triangle]dp[0][0] = triangle[0][0]for i in range(1, len(triangle)): for j in range(len(triangle[i])): if j == 0: dp[i][j] = dp[i - 1][j] + triangle[i][j] elif j == len(triangle[i]) - 1: dp[i][j] = dp[i - 1][j - 1] + triangle[i][j] else: dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + triangle[i][j]
3) 배낭 문제(0/1 Knapsack) 템플릿
# weight[i], value[i], 최대 무게 Wdp = [[0] * (W + 1) for _ in range(n + 1)]for i in range(1, n + 1): w, v = weight[i], value[i] for cur_w in range(0, W + 1): if cur_w < w: dp[i][cur_w] = dp[i - 1][cur_w] else: dp[i][cur_w] = max(dp[i - 1][cur_w], dp[i - 1][cur_w - w] + v)
해시 / 정렬
1) 빈도수 세기 패턴
from collections import Countercnt = Counter(arr)for k, v in cnt.items(): pass
2) 딕셔너리로 카운팅
d = {}for x in arr: d[x] = d.get(x, 0) + 1
3) 정렬 key 템플릿
# 단일 기준arr.sort(key=lambda x: x[1])# 복합 기준arr.sort(key=lambda x: (x[0], -x[1]))
우선순위 큐 & 힙
1) 최소 힙 기본
import heapqhq = []heapq.heappush(hq, (priority, data))priority, data = heapq.heappop(hq)
jobs.sort() # 도착 시간 기준 정렬time = 0idx = 0import heapqhq = []while idx < len(jobs) or hq: while idx < len(jobs) and jobs[idx][0] <= time: start, duration = jobs[idx] heapq.heappush(hq, (duration, start)) idx += 1 if hq: duration, start = heapq.heappop(hq) time += duration else: time = jobs[idx][0]
투 포인터 (Two Pointers)
1) 두 수의 합 (정렬 필요)
arr.sort()i, j = 0, len(arr) - 1while i < j: s = arr[i] + arr[j] if s == target: # 정답 처리 break if s < target: i += 1 else: j -= 1
2) 슬라이딩 윈도우(연속 부분합, 고정 길이)
k = 3window_sum = sum(arr[:k])best = window_sumfor i in range(k, len(arr)): window_sum += arr[i] - arr[i - k] best = max(best, window_sum)
3) 슬라이딩 윈도우(연속 부분합, 가변 길이, 합 ≤ S)
left = 0cur_sum = 0ans = 0for right in range(len(arr)): cur_sum += arr[right] while cur_sum > S: cur_sum -= arr[left] left += 1 ans = max(ans, right - left + 1)
이분 탐색 (Binary Search)
1) 값 직접 찾기
def binary_search(arr, target): lo, hi = 0, len(arr) - 1 while lo <= hi: mid = (lo + hi) // 2 if arr[mid] == target: return mid if arr[mid] < target: lo = mid + 1 else: hi = mid - 1 return -1
2) lower_bound / upper_bound
from bisect import bisect_left, bisect_rightleft = bisect_left(arr, x) # x 이상 처음 위치right = bisect_right(arr, x) # x 초과 처음 위치cnt = right - left
3) Parametric Search(정답 범위에 대한 이분 탐색)
def can(mid): # mid가 조건을 만족하면 True, 아니면 False return Truelo, hi = 0, MAXans = hiwhile lo <= hi: mid = (lo + hi) // 2 if can(mid): ans = mid hi = mid - 1 # 최소값 찾기 else: lo = mid + 1
트리 / 그래프 기본
1) 인접 리스트 생성
graph = [[] for _ in range(n + 1)]for a, b in edges: graph[a].append(b) graph[b].append(a)
2) 트리에서 부모 찾기 (DFS)
parent = [0] * (n + 1)visited = [False] * (n + 1)def dfs(x): visited[x] = True for nxt in graph[x]: if not visited[nxt]: parent[nxt] = x dfs(nxt)dfs(1)
3) Union-Find (Disjoint Set)
parent = [i for i in range(n + 1)]def find(x): if parent[x] != x: parent[x] = find(parent[x]) return parent[x]def union(a, b): ra, rb = find(a), find(b) if ra != rb: if ra < rb: parent[rb] = ra else: parent[ra] = rb
4) Kruskal MST
edges.sort(key=lambda x: x[2]) # (u, v, w)mst = 0for u, v, w in edges: if find(u) != find(v): union(u, v) mst += w
구현 문제 팁 (문자열/좌표)
1) 문자열 자주 쓰는 패턴
s = s.strip()s = s.replace("a", "b")parts = s.split(":")joined = "-".join(parts)
2) 좌표 시뮬레이션 기본
dx = [1, -1, 0, 0]dy = [0, 0, 1, -1]x, y = sx, syfor cmd in commands: if cmd == "U": nx, ny = x - 1, y elif cmd == "D": nx, ny = x + 1, y # ... if 0 <= nx < n and 0 <= ny < m: x, y = nx, ny