Midterm Study Guide · 2026 Spring

알고리즘
중간고사 완전정복

초보자도 이해할 수 있는, 그러나 시험에서 100점을 받을 수 있을 만큼 상세한 정리본. 기출 문제 패턴, 교수님 정리 세션, 과제 해답을 모두 반영했다.

ScopeCh. 1–16, 21 + α Topics25+ 핵심 주제 Target100 / 100

이 문서를 효율적으로 쓰는 법

이 가이드는 위에서 아래로 순서대로 읽는 것을 권장한다. 앞 개념이 뒷 개념의 전제가 되기 때문이다. 예를 들어 "지시 확률 변수"를 먼저 이해해야 "무작위 퀵 정렬의 O(n log n) 증명"을 이해할 수 있다.

각 주제에는 티어 표시가 붙어있다.

  • TIER S — 기출 4회 이상 출제. 무조건 완벽하게 쓸 수 있어야 함.
  • TIER A — 정리 세션에서 강조됨. 신규 출제 가능성 높음.
  • TIER B — 개념을 이해하고 설명할 수 있는 수준.
Core Mental Model

"이 문제를 풀 수 있는가?"가 아니라 "왜 이 기법이 작동하는가?"를 물어라. 알고리즘 시험은 테크닉 대회가 아니라, 구조적 통찰을 검증하는 시험이다.

Part I

기초 개념

모든 알고리즘 논의가 딛고 서는 토대. 문제/사례의 구별, 점근적 표기법, 점화식 풀이 세 가지를 명확히 하자.

1 · 문제와 사례의 구별

문제(Problem)는 일반적이고 추상적인 질문이며, 사례(Instance)는 그 문제에 구체적인 입력을 넣은 것이다.

Definition

문제: "정렬되지 않은 n개의 정수를 오름차순으로 정렬하라"

사례: "[3, 1, 4, 1, 5, 9]를 오름차순으로 정렬하라"

왜 이 구별이 중요한가

같은 알고리즘이라도 어떤 사례가 주어지느냐에 따라 성능이 달라진다. 삽입 정렬은 이미 정렬된 배열(최선의 사례)에 대해 O(n)이지만, 역순 배열(최악의 사례)에 대해 O(n²)이다. 알고리즘을 분석한다는 것은 곧 "가능한 모든 사례에 대해 어떤 성능 분포를 갖는가"를 따지는 일이다.

계산 가능한 문제와 불가능한 문제

계산 문제의 집합은 비가산(uncountable), 즉 양의 정수와 일대일 대응될 수 없는 무한 집합이다. 반면 모든 알고리즘의 집합은 가산 무한(countably infinite)이다. 이 두 사실로부터 수학적으로 귀결되는 것: 알고리즘으로 해결 불가능한 문제가 반드시 존재한다.

대표 예시 · Halting Problem

"주어진 프로그램 P와 입력 x에 대해, P(x)가 언젠가 정지하는가?" — 이 질문에 정답을 내놓는 알고리즘은 존재하지 않는다. 이를 정지 문제가 계산 불가능하다고 한다.

시험 팁

이 수업에서는 정지 문제의 불가능성을 직접 증명하지 않는다. "계산 불가능한 문제가 존재한다"와 "Halting Problem이 그 예시다"만 외워도 충분하다.

2 · 점근적 표기법

n이 무한히 커질 때, 함수가 얼마나 빠르게 증가하는가를 비교하는 도구.

두 알고리즘의 실행 시간이 이라면, n → ∞에서 이 훨씬 느리게 커지므로 을 선택한다. 점근적 표기법은 이러한 비교를 형식화한다.

세 가지 표기법

Big-O · 상한

$f(n) = O(g(n))$: 어떤 상수 c > 0n₀이 존재해서 모든 n ≥ n₀에 대해 0 ≤ f(n) ≤ c · g(n)이 성립한다.

즉, fg보다 빠르지 않게 자란다는 뜻.

Big-Ω · 하한

$f(n) = \Omega(g(n))$: 모든 n ≥ n₀에 대해 0 ≤ c · g(n) ≤ f(n).

fg보다 느리지 않게 자란다.

Theta · 타이트한 경계

$f(n) = \Theta(g(n))$: Big-O이자 Big-Ω. 즉 c₁ · g(n) ≤ f(n) ≤ c₂ · g(n).

fg같은 속도로 자란다.

다항식 관련 개념

다항 시간(polynomial time)이란 O(n^k) 꼴의 시간 복잡도를 말한다. 효율적인 알고리즘의 기준으로 흔히 쓰인다.

주의 · Simplex Algorithm

심플렉스 알고리즘은 최악의 경우에는 다항 시간이 아니다. 그러나 실제로는 다른 알고리즘보다 훨씬 빠르게 동작한다. 이것이 "점근적 분석이 만능은 아니다"를 보여주는 대표 사례다. (기말고사 29장에서 다룸)

3 · 점화식과 재귀 트리

재귀 알고리즘의 시간 복잡도를 계산하는 핵심 도구.

점화식이란

분할 정복 같은 재귀 알고리즘의 실행 시간은 자기 자신을 포함하는 식으로 표현된다. 예를 들어 병합 정렬의 실행 시간은:

$$T(n) = \begin{cases} c & \text{if } n = 1 \\ 2T(n/2) + cn & \text{if } n > 1 \end{cases}$$

분할: 상수 시간, 정복: 자신을 두 번 호출 (크기 n/2씩), 결합(merge): 선형 시간.

재귀 트리로 풀기

점화식 T(n) = 2T(n/2) + cn을 재귀 트리로 전개하자.

cn cost: cn cn/2 cn/2 cost: cn cn/4 cn/4 cn/4 cn/4 cost: cn log n 레벨 cost: cn 크기 1 리프: n개 cost: cn (n × c) 총 비용: cn × (log n + 1) = cn log n + cn
Merge Sort 재귀 트리 — 각 레벨의 총 비용은 cn으로 일정

각 레벨의 총 비용이 cn으로 동일하다. 레벨 수는 log₂ n + 1개이므로:

결론

$$T(n) = cn \log n + cn = \Theta(n \log n)$$

시험 팁

재귀 트리를 그리는 것 = 공식을 외우지 않고 왜 그 결과가 나오는지 보여주는 방법이다. 시험에서 점화식 문제가 나오면 재귀 트리부터 그려라. 이게 가장 안전하다.

Part II

정렬 알고리즘

다섯 가지 설계 기법(incremental, divide-and-conquer, using data structure, DP, greedy) 중 세 가지가 정렬에서 만난다. 각 알고리즘의 시간 복잡도 분석을 말로 설명할 수 있어야 한다.

4 · 삽입 정렬 (Insertion Sort)

정렬된 부분 배열에 새 원소를 하나씩 끼워넣어가는 방식. 인간이 카드를 정렬하는 방식과 동일하다.

의사코드

INSERTION-SORT(A)
  for j = 2 to A.length
    key = A[j]
    // A[j]를 정렬된 A[1..j-1]에 삽입
    i = j - 1
    while i > 0 and A[i] > key
      A[i+1] = A[i]
      i = i - 1
    A[i+1] = key

시간 복잡도 분석

경우사례복잡도
최선이미 정렬된 배열 [1,2,3,4,5]Θ(n)
최악역순 배열 [5,4,3,2,1]Θ(n²)
평균무작위 순열Θ(n²)

왜 최악이 인가? 역순일 때 매번 while 루프가 j-1번 돌아간다. 전체는 1 + 2 + … + (n-1) = n(n-1)/2 = Θ(n²).

5 · 병합 정렬 (Merge Sort)

분할(Divide) → 정복(Conquer) → 결합(Combine) 3단계의 대표 예시. 최악의 경우에도 Θ(n log n)을 보장한다.

세 단계

  1. 분할: 배열을 반으로 나눈다. (상수 시간)
  2. 정복: 각 절반을 재귀적으로 정렬한다. (2T(n/2))
  3. 결합: 정렬된 두 부분을 MERGE로 합친다. (Θ(n))

이로부터 점화식:

$$T(n) = 2T(n/2) + \Theta(n) \implies T(n) = \Theta(n \log n)$$

대부분의 작업은 결합 단계에서 일어난다. 이것이 퀵 정렬과의 큰 차이점이다 (퀵 정렬은 분할에서 대부분을 한다).

6 · 힙 정렬과 Max-Heap

거의 완전한 이진 트리 구조인 Max-heap을 이용해 정렬하는 방식. 최악 O(n log n) 보장.

Max-Heap 특성

Max-heap property

부모 노드 ≥ 자식 노드. 즉 루트에 최댓값이 있다.

구조적으로는 거의 완전한 이진 트리(almost complete binary tree). 마지막 레벨을 제외하면 모든 레벨이 꽉 차 있고, 마지막 레벨은 왼쪽부터 채워진다.

세 가지 알고리즘

① Max-Heapify(A, i)

전제 조건: 인덱스 i의 좌우 자식 서브트리는 각각 max-heap이다. 그러나 i 자체는 아닐 수 있다.

목표: i를 루트로 하는 서브트리 전체를 max-heap으로 만든다.

방법: i와 두 자식 중 가장 큰 값을 찾는다. i가 가장 크면 끝. 아니면 가장 큰 자식과 교환하고, 교환된 자식 위치에서 재귀 호출.

시간 복잡도: 노드 i의 높이에 비례하므로 O(log n).

② Build-Max-Heap(A)

배열 A를 max-heap으로 만드는 알고리즘. 인덱스 ⌊n/2⌋부터 1까지 역순으로 Max-Heapify를 호출한다.

BUILD-MAX-HEAP(A)
  A.heap-size = A.length
  for i = ⌊A.length/2downto 1
    MAX-HEAPIFY(A, i)
놀라운 사실

Build-Max-Heap은 선형 시간 O(n)이다. 단순 분석으로는 O(n log n)처럼 보이지만, 실제로는 높이 h인 노드는 ⌈n/2^(h+1)⌉개이고 각각 O(h)의 작업을 하므로 총합이 O(n)이 된다.

③ Heapsort(A)
  1. Build-Max-Heap으로 최대 힙 구축: O(n)
  2. 루트(최댓값)를 마지막 원소와 교환, heap-size 감소
  3. Max-Heapify(A, 1) 호출: O(log n)
  4. 2–3을 n-1번 반복

총 시간: O(n) + (n-1) · O(log n) = O(n log n).

주요 수량 (증명 없이 사용)

  • n개 원소를 가진 힙의 높이 = ⌊log₂ n⌋, 즉 O(log n)
  • 높이 h인 노드의 최대 개수 = ⌈n / 2^(h+1)⌉

7 · 퀵 정렬 (Quicksort)

피벗(pivot)을 기준으로 분할하는 알고리즘. 최악은 O(n²)이지만, 평균 O(n log n)이고 상수 계수가 작아 실무에서 가장 선호된다.

핵심 아이디어

피벗 원소를 하나 고른 뒤, 피벗보다 작은 것은 왼쪽, 큰 것은 오른쪽으로 보낸다. 이러면 피벗이 최종 정렬 위치에 놓인다. 이 분할을 재귀적으로 왼쪽/오른쪽 부분 배열에 적용한다.

Partition 과정 (선형 시간)

PARTITION(A, p, r)
  x = A[r]        // 피벗 = 마지막 원소
  i = p - 1
  for j = p to r-1
    if A[j] <= x
      i = i + 1
      swap(A[i], A[j])
  swap(A[i+1], A[r])
  return i+1     // 피벗의 최종 위치

시간 복잡도는 피벗 선택에 달려 있다

최선 · 중간값이 피벗

T(n) = 2T(n/2) + Θ(n)Θ(n log n)

최악 · 최대/최소가 피벗

T(n) = T(n-1) + Θ(n)Θ(n²)

최악이 발생할 확률은 매우 낮지만 0은 아니다. 이를 피하는 기법이 무작위 추출(random sampling), 즉 무작위 퀵 정렬이다.

기출 단골

"정렬되지 않은 배열 [2, 1, 3, 4, 10, 12, 6, 9, 5]에 대해 Quicksort의 pivot 기준 좌우 분할 과정을 단계별로 서술하라"가 2023-1 중간에 출제됨. 매번 배열이 바뀌므로 Partition이 어떻게 동작하는지를 중심으로 서술하면 된다.

Part III

확률적 분석

최악의 경우도, 최선의 경우도 아닌 기대값에 관심이 있는 분석. 지시 확률 변수(Indicator RV)가 주인공이다.

8 · 고용 문제 (Hiring Problem)

n명의 지원자를 순서대로 면접한다. 현재까지 본 사람 중 가장 뛰어난 사람이 나오면 고용한다 (이전 사람은 해고). 고용 횟수의 기대값은?

세팅

  • 입력: n명의 지원자
  • 가정: 지원자들이 등장하는 순서는 균등 무작위. 즉 n!가지 순열이 모두 동등 확률.
  • 구하고 싶은 것: 고용 횟수 X의 기대값 E[X]

지시 확률 변수로 분해

X_i를 "i번째 지원자를 고용하면 1, 아니면 0"으로 정의한다. 그러면:

$$X = X_1 + X_2 + \cdots + X_n$$

E[X_i]는 무엇인가?

i번째 지원자를 고용하려면, 그 사람이 앞 i명 중 가장 뛰어나야 한다. 순서가 무작위이므로 이 확률은 정확히 1/i.

기댓값의 선형성(Linearity of Expectation)을 쓰면:

$$E[X] = \sum_{i=1}^{n} E[X_i] = \sum_{i=1}^{n} \frac{1}{i} = H_n = O(\log n)$$

(조화수 H_nln n + O(1))

Why this matters

"1등부터 봐서 매번 교체될 것 같은" 직관은 n번 고용을 예상하지만, 실제로는 log n번만 고용한다. 이것이 확률적 분석의 힘이다.

9 · 지시 확률 변수 (Indicator Random Variable)

복잡한 확률 변수를 "0 또는 1"의 단순한 변수들의 합으로 분해하는 기법.

Definition

사건 A가 일어날 때 1, 아닐 때 0인 확률 변수를 I{A}라 한다.

$$E[I\{A\}] = 1 \cdot \Pr(A) + 0 \cdot \Pr(\bar A) = \Pr(A)$$

기대값의 선형성

$$E[X_1 + X_2 + \cdots + X_n] = E[X_1] + E[X_2] + \cdots + E[X_n]$$

중요: 독립성이 필요 없다. 변수가 서로 종속이어도 합의 기대값은 기대값의 합이다.

이 두 도구 — 지시 변수 + 선형성 — 만으로 많은 어려운 기대값을 계산할 수 있다.

10 · 무작위 퀵 정렬의 O(n log n) 증명 ⭐

2025 여름학기에 indicator RV 사용을 명시적으로 요구함. 증명 전체를 암기할 수준으로 준비해야 한다.

세팅

입력: 서로 다른 n개의 정수. 피벗은 매번 남은 원소 중에서 균등 무작위 선택. 분석 대상: 두 원소가 비교되는 총 횟수 X. (Lemma 7.1에 의해 전체 실행 시간은 O(n + X).)

증명의 핵심 단계

  1. 원소를 재명명한다. 정렬 후의 순서대로 z_1, z_2, …, z_n으로 부르자. 즉 z_ii번째로 작은 원소.
  2. 지시 변수를 정의: X_{ij} = "z_iz_j가 비교되면 1, 아니면 0"
  3. 총 비교 횟수: $$X = \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} X_{ij}$$
  4. 핵심 관찰: z_iz_j는 언제 비교되는가? 퀵 정렬에서 두 원소가 비교되는 유일한 경우는 둘 중 하나가 피벗일 때다.

    집합 Z_{ij} = {z_i, z_{i+1}, …, z_j}를 생각하자. 이 집합에서 최초로 피벗으로 뽑히는 원소z_i 또는 z_j일 때만 두 원소가 비교된다. 그렇지 않고 중간 원소가 먼저 피벗이 되면, z_iz_j는 서로 다른 부분 배열로 분리되어 영원히 비교되지 않는다.
  5. |Z_{ij}| = j - i + 1개 중 하나가 균등 확률로 먼저 피벗이 되므로: $$E[X_{ij}] = \Pr(X_{ij} = 1) = \frac{2}{j - i + 1}$$
  6. 기댓값의 선형성: $$E[X] = \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} \frac{2}{j-i+1}$$
  7. 변수 치환 k = j - i: $$E[X] = \sum_{i=1}^{n-1} \sum_{k=1}^{n-i} \frac{2}{k+1} < \sum_{i=1}^{n-1} \sum_{k=1}^{n} \frac{2}{k} = \sum_{i=1}^{n-1} O(\log n) = O(n \log n)$$
최종 결과

$$E[X] = O(n \log n)$$

따라서 무작위 퀵 정렬의 기대 실행 시간은 O(n \log n).

답안 작성 팁

시험에서 이 증명을 쓸 때, 단계 4의 "최초로 피벗이 되는 원소" 설명이 가장 많이 빠진다. 이 부분을 반드시 써야2/(j-i+1)이 나오는지 설명된다.

Part IV

동적 계획법

두 가지 구조적 특성(optimal substructure + overlapping subproblems)을 가진 최적화 문제에 적용되는 강력한 기법.

11 · DP의 두 가지 특성

① Optimal Substructure

문제의 최적해가 부분 문제들의 최적해로 구성된다.

② Overlapping Subproblems

같은 부분 문제를 반복해서 다시 풀어야 하는 상황이 발생한다.

세 가지 특성 한눈에 비교

특성의미필요한 기법
Optimal substructure최적해가 부분해의 최적해로 구성DP, Greedy 모두
Overlapping subproblems같은 부분 문제 반복DP (테이블로 캐시)
Greedy choice순간의 최선 선택이 전체 최적Greedy만

Optimal Substructure 증명의 표준 패턴

  1. "최적해가 주어졌다고 가정하자"
  2. "이 최적해는 부분 문제들의 최적해로 구성되어야 한다"고 주장
  3. "그렇지 않다면 모순이 발생한다"고 증명
왜 "cut-and-paste" 증명인가

보통 2-3 단계에서, 부분 해를 더 좋은 것으로 잘라서 붙이면 전체가 더 좋아진다는 논리를 쓴다. 이것이 원래 해가 최적이라는 가정과 모순이다.

12 · 행렬 연쇄 곱셈 ⭐

2024-2, 2025-1, 2025-S 중간고사에 동일한 숫자로 3회 연속 출제. 답을 외워두는 것이 안전하다.

문제

행렬 A₁, A₂, A₃, A₄를 곱하는 스칼라 곱셈의 총 횟수를 최소화하자.

  • A₁: 2 × 3 행렬
  • A₂: 3 × 4 행렬
  • A₃: 4 × 2 행렬
  • A₄: 2 × 3 행렬

차원 수열: p₀=2, p₁=3, p₂=4, p₃=2, p₄=3

점화식

$$m[i,j] = \begin{cases} 0 & \text{if } i = j \\ \min_{i \le k < j} \{ m[i,k] + m[k+1,j] + p_{i-1} \cdot p_k \cdot p_j \} & \text{if } i < j \end{cases}$$

두 개의 그룹 A_i…A_kA_{k+1}…A_j로 분할하는 모든 방법을 시도하고, 그 중 최소를 고른다.

테이블 채우기 (대각선 순서)

먼저 대각선(i=j)은 모두 0. 그다음 길이 2, 3, 4 순으로 채운다.

길이 2

m[1,2] = p₀·p₁·p₂ = 2·3·4 = 24

m[2,3] = p₁·p₂·p₃ = 3·4·2 = 24

m[3,4] = p₂·p₃·p₄ = 4·2·3 = 24

길이 3

m[1,3]: k=1 또는 k=2

  • k=1: m[1,1] + m[2,3] + p₀p₁p₃ = 0 + 24 + 2·3·2 = 36
  • k=2: m[1,2] + m[3,3] + p₀p₂p₃ = 24 + 0 + 2·4·2 = 40

m[1,3] = 36 (k=1)

m[2,4]: k=2 또는 k=3

  • k=2: m[2,2] + m[3,4] + p₁p₂p₄ = 0 + 24 + 3·4·3 = 60
  • k=3: m[2,3] + m[4,4] + p₁p₃p₄ = 24 + 0 + 3·2·3 = 42

m[2,4] = 42 (k=3)

길이 4

m[1,4]: k=1, 2, 또는 3

  • k=1: m[1,1] + m[2,4] + p₀p₁p₄ = 0 + 42 + 2·3·3 = 60
  • k=2: m[1,2] + m[3,4] + p₀p₂p₄ = 24 + 24 + 2·4·3 = 72
  • k=3: m[1,3] + m[4,4] + p₀p₃p₄ = 36 + 0 + 2·2·3 = 48

m[1,4] = 48 (k=3)

최종 테이블

m[i,j] j=1j=2j=3j=4
i=1 0243648
i=2 02442
i=3 024
i=4 0
최종 답

최소 스칼라 곱셈 횟수 = 48

최적 괄호화: ((A₁ (A₂ A₃)) A₄) — k=3에서 분할, 그 안에서 k=1로 분할

Optimal Substructure 증명

최적 괄호화가 (A_i … A_k)(A_{k+1} … A_j)로 분할된다고 하자. 주장: 이 분할 안에서 A_i … A_kA_{k+1} … A_j 각각의 괄호화도 최적이어야 한다.

만약 A_i … A_k의 괄호화가 최적이 아니라면, 더 적은 스칼라 곱셈으로 할 수 있는 괄호화가 존재한다. 그걸로 교체하면 전체 비용이 감소하여 원래 괄호화가 최적이라는 가정에 모순. □

13 · 최장 공통 부분수열 (LCS)

두 문자열 X = x₁x₂…x_m, Y = y₁y₂…y_n의 공통 부분수열 중 가장 긴 것을 찾는 문제.

점화식

$$c[i,j] = \begin{cases} 0 & \text{if } i = 0 \text{ or } j = 0 \\ c[i-1,j-1] + 1 & \text{if } i, j > 0 \text{ and } x_i = y_j \\ \max(c[i-1,j], c[i,j-1]) & \text{if } i, j > 0 \text{ and } x_i \ne y_j \end{cases}$$

예시 · HW1 문제

X = GTACCGTCA (가로), Y = TCAGCTCA… 아니 HW1에서는 Y = TCGA 같은 형태. 테이블로 표시하면:

GTACCGTCA
0000000000
T0011111111
C0011222222
G0111223333
A0112223334

LCS 길이 = 4. 역추적하면 LCS = TCGA.

역추적 방법

우하단에서 시작. x_i = y_j면 대각선 위로 이동하며 문자 기록. 아니면 더 큰 값 쪽(위 또는 왼쪽)으로 이동. 경계까지 가면 기록된 문자들이 LCS (역순으로 읽기).

14 · 0/1 배낭 문제의 Optimal Substructure

용량 W인 배낭. n개 물건 각각 무게 w_i, 가치 v_i. 물건은 전부 가져가거나 전혀 가져가지 않거나. 총 가치 최대화.

Optimal Substructure 증명

최적해 S가 주어졌다고 하자. S에서 임의의 물건 j를 골라 두 경우로 나눈다:

Case 1 · j ∈ S (물건 j가 최적해에 포함)

S \ \{j\}는 "물건 {1, 2, …, n} \ \{j\}, 용량 W - w_j"인 부분 문제의 최적해여야 한다.

증명: 만약 더 좋은 해 S'가 존재한다면, S' ∪ \{j\}는 원래 문제의 해이면서 S보다 가치가 높다. 모순.

Case 2 · j ∉ S (물건 j가 최적해에 포함되지 않음)

S는 "물건 {1, …, n} \ \{j\}, 용량 W"인 부분 문제의 최적해여야 한다.

같은 논리로 모순 도출. □

분할 배낭 문제와의 차이

분할(Fractional) 배낭은 물건을 쪼갤 수 있다. 증명 구조가 다르다:

최적해에 물건 jx 비율이 포함되어 있다면, 나머지 해는 "물건 {1, …, n} \ \{j\} + j(1-x) 비율, 용량 W - x·w_j"인 부분 문제의 최적해여야 한다.

결정적 차이

분할 배낭은 greedy choice property도 가져서 탐욕 알고리즘으로 풀린다 (가치/무게 비율 높은 것부터). 0/1 배낭은 greedy choice가 없다. DP가 필수.

Part V

탐욕 알고리즘

"지금 이 순간 최선으로 보이는 것을 고른다"는 단순한 원리. 문제가 greedy choice 특성을 가질 때만 작동한다.

15 · 활동 선택 문제 ⭐

n개 활동 각각이 시작시간 s_i, 종료시간 f_i를 가진다. 서로 겹치지 않는 활동의 최대 개수를 고르자.

탐욕 전략

핵심 선택 규칙

종료 시간이 가장 이른 활동을 먼저 고른다. (시작 시간이 아님!)

Greedy Choice Property 증명 (Theorem 16.1)

활동을 종료 시간 순으로 정렬: f_1 ≤ f_2 ≤ … ≤ f_n. 주장: 어떤 최적해 A가 존재하는데, 활동 1이 그 최적해에 포함된다.

증명 (교환 논법)
  1. 임의의 최적해 A를 생각하자. A의 활동을 종료시간 순 정렬하여 첫 원소를 k라 하자.
  2. 만약 k = 1이면 끝. 활동 1이 이미 포함됨.
  3. 만약 k ≠ 1이면, A' = (A \ \{k\}) ∪ \{1\}을 만들자.
  4. f_1 ≤ f_k이므로 활동 1은 A \ \{k\}의 다른 활동들과 겹치지 않는다 (원래 k가 첫 번째였으므로 다른 모든 활동은 k 이후에 시작, 따라서 1 이후에도 시작).
  5. |A'| = |A|이므로 A'도 최적해. 활동 1을 포함. □
Why early finish?

가장 빨리 끝나는 활동을 고르면, 남은 시간 자원이 최대화된다. 이것이 "순간의 최선"이 전체 최적으로 이어지는 이유다.

알고리즘 구현 · 재귀적 선택기

RECURSIVE-ACTIVITY-SELECTOR(s, f, k, n)
  m = k + 1
  while m <= n and s[m] < f[k]   // 겹치면 건너뜀
    m = m + 1
  if m <= n
    return {a_m} ∪ RECURSIVE-ACTIVITY-SELECTOR(s, f, m, n)
  else
    return

시간 복잡도: O(n) — 시작/종료 시간을 한 번씩만 스캔.

16 · 매트로이드 (Matroid)

"어떤 문제가 탐욕 알고리즘으로 풀리는가?"에 대한 구조적 답. 문제의 기저 구조가 매트로이드이면 greedy가 작동한다.

정의

Matroid

순서쌍 M = (S, I)가 매트로이드이려면:

  1. S는 공집합이 아닌 유한 집합
  2. IS의 부분집합들의 모임 (independent set family) 으로 다음 두 조건을 만족:
    • Hereditary 성질: X ∈ I이고 Y ⊆ X이면 Y ∈ I
    • Exchange 성질: X, Y ∈ I이고 |X| > |Y|이면, 어떤 a ∈ X \ Y가 존재해서 Y ∪ \{a\} ∈ I

핵심 정리

최적화 문제의 기저 구조가 매트로이드이면, 그 문제는 탐욕 알고리즘으로 해결 가능하다.

역은 성립하지 않는다! 탐욕으로 풀리는 문제가 반드시 매트로이드 구조인 것은 아니다. (예: 활동 선택 문제는 매트로이드가 아니지만 탐욕으로 풀린다.)

Graphic Matroid

Definition

무방향 그래프 G = (V, E)가 주어졌을 때:

  • S = E (간선 집합)
  • I = \{A ⊆ E : A사이클을 포함하지 않음 (forest)\}

이 구조가 매트로이드임을 증명하려면 hereditary와 exchange 두 조건을 보여야 한다.

Hereditary 증명

A ∈ I(사이클 없음)이고 B ⊆ A이면, B도 사이클 없음 (사이클이 있다면 A에도 있어야 하므로 모순). ∴ B ∈ I. □

Exchange 증명

A, B ∈ I이고 |A| > |B|. A, B는 각각 forest. |V| - |A|, |V| - |B|는 각각 A, B의 컴포넌트 수. |A| > |B|이므로 A의 컴포넌트 수가 B보다 적다. 따라서 B의 두 다른 컴포넌트에 걸쳐 있는 A의 간선 e가 존재한다. 이 eB에 추가해도 사이클이 생기지 않는다. ∴ B ∪ \{e\} ∈ I. □

17 · 단위 작업 스케줄링이 매트로이드임 ⭐⭐

중간고사에서 가장 자주 나오는 증명. 2023-1, 2024-2, 2024-W, 2025-S 중간 + 2025-S 기말까지 5회 출제.

문제 세팅

n개 작업. 각 작업은 단위 시간이 걸리고, 마감 시간 d_i와 벌점 w_i를 가진다. 마감을 넘기면 벌점 부과. 총 벌점을 최소화하자. (동치로: 마감 안에 끝낸 작업의 총 벌점을 최대화)

매트로이드 구조

  • S = 모든 작업의 집합 \{a_1, …, a_n\}
  • I = "작업들을 모두 마감 안에 끝낼 수 있는 부분집합들"

A ∈ IA를 어떤 순서로 처리하면 모두 마감을 지킬 수 있다.

성질 1 · Hereditary

A ∈ I이고 B ⊆ A이면, B의 모든 작업도 마감 전에 끝낼 수 있다 (A의 스케줄에서 B 외의 작업을 제거하면 되며, 앞당겨지면 더 일찍 끝나므로 여전히 마감 안). ∴ B ∈ I. □

성질 2 · Exchange

A, B ∈ I이고 |A| > |B|. 각각의 최적 스케줄을 Ak=|A|개 타임슬롯, B|B|개 타임슬롯에 배치했다고 하자.

핵심 보조 정리: 어떤 작업 집합 X ∈ I에 대해, 작업들을 마감 시간 증가 순으로 정렬해서 순서대로 배치하면, 그것이 정확히 |X|개 시간 슬롯을 쓰는 유효 스케줄이 된다.

|A| > |B|이므로, AB의 정렬된 스케줄을 비교하면 A에만 있고 B에 없는 작업이 있다. 그 중 가장 늦은 시간대에 스케줄된 작업 a를 고르자. 이 작업은 어떤 시간 t > |B|에 배치되어 있다(B의 스케줄은 시간 |B|까지만 사용).

a의 마감은 d_a ≥ t > |B|. 따라서 aB|B|+1번째 타임슬롯에 추가해도 마감을 어기지 않는다. ∴ B ∪ \{a\} ∈ I. □

답안 작성 팁

시험에서는 "정렬된 스케줄" 논리가 가장 중요하다. 작업을 마감 순으로 정렬해서 배치하면 유효 스케줄이 된다는 사실을 명시적으로 쓰자. 그 다음 A에만 있는 늦은 작업을 B에 추가할 수 있음을 보이면 exchange 증명 완성.

Part VI

무작위 알고리즘

교재에 없는 특별 주제. 과제 자료에서 Freivalds 알고리즘, 정리 세션에서 Tutte·BDT를 다뤘다.

18 · Freivalds 알고리즘 ⭐

"AB = C인가?"를 직접 곱하지 않고 확률적으로 검증하는 기발한 알고리즘. O(n²) 시간.

문제

n × n 행렬 A, B, C가 주어졌을 때, AB = C인지 판정.

단순 접근: AB를 직접 계산 — O(n³). Strassen으로도 O(n^{2.81}).

Freivalds: O(n²). 단, 확률적 오류 가능.

알고리즘

  1. x ∈ \{0,1\}^n을 균등 무작위로 선택 (각 성분이 0 또는 1)
  2. ABx ≠ Cx이면 NO 반환, 아니면 YES 반환

O(n²)인가? BxO(n²), 그 결과에 A를 곱하는 것도 O(n²), CxO(n²). 전체 O(n²).

오류 분석

한쪽 방향 오류만 있다. AB = C이면 항상 YES (정확). AB ≠ C인데 알고리즘이 YES라고 답할 확률은?

주 정리

$$\Pr(ABx = Cx \mid AB \ne C) \le \frac{1}{2}$$

증명

  1. D = AB - C로 정의. AB ≠ C이므로 D는 영행렬이 아님. 적어도 하나의 성분이 0이 아니다.
  2. 일반성을 잃지 않고 가정할 수 있다 (Without Loss of Generality):
    • 0이 아닌 성분이 D첫 번째 행에 있다
    • 첫 번째 행에서 0이 아닌 성분들이 0들보다 앞서 있다
    • 즉 첫 번째 행이 (d_1, d_2, …, d_k, 0, 0, …, 0) 꼴 (k > 0)
  3. Dx = 0이 되려면 특히 첫 번째 성분 d^T x = 0이어야 한다. 여기서 dD의 첫 행이고: $$d^T x = d_1 x_1 + d_2 x_2 + \cdots + d_k x_k = 0$$
  4. 정리하면: $$x_1 = \frac{-d_2 x_2 - d_3 x_3 - \cdots - d_k x_k}{d_1}$$
  5. 결정적 관찰: x_2, x_3, …, x_k를 먼저 뽑은 뒤 x_1을 뽑는다고 생각하자. 위 식의 우변은 x_2, …, x_k에 의해 고정된 어떤 값 v다.
  6. x_1\{0, 1\}에서 균등 무작위로 뽑히므로, x_1 = v일 확률은 많아야 1/2. (v가 0이나 1이 아니면 확률 0, 그중 하나이면 확률 1/2)
  7. 따라서 \Pr(Dx = 0) ≤ 1/2. □

오류 감소

알고리즘을 k번 반복하고, 모두 YES일 때만 YES로 답하면 오류 확률은 (1/2)^k로 지수적으로 감소. 20번 반복하면 오류 확률 < 0.0001%.

19 · Tutte 행렬과 완벽 매칭

좌/우 집합 크기가 같은 이분 그래프에서 완벽 매칭(perfect matching)이 존재하는지 확률적으로 판정.

Tutte 행렬

이분 그래프 G = (L ∪ R, E), |L| = |R| = n. Tutte 행렬 Tn × n:

  • T[i][j] = x_{ij} (변수) · 만약 (L_i, R_j) ∈ E
  • T[i][j] = 0 · 아니면
Tutte 정리

이분 그래프 G가 완벽 매칭을 가진다 T의 행렬식이 (변수 다항식으로서) 항등적으로 0이 아니다.

알고리즘

  1. 각 변수 x_{ij}\{1, 2, …, m²\}에서 무작위 정수 대입 (m = 간선 수)
  2. 행렬식 계산
  3. 0이 아니면 YES, 0이면 NO

오류 분석

False Negative (한 방향 오류)

완벽 매칭이 없으면 → 행렬식이 항상 0 → 항상 NO 반환 (정확)

완벽 매칭이 있으면 → 행렬식이 항등적 0은 아님 → 그러나 무작위 대입 시 우연히 0이 될 수 있음 → NO 반환 가능 (오답)

이 오류는 반복으로 감소 가능. 여러 번 시도해서 한 번이라도 YES가 나오면 YES.

20 · 이진 결정 트리 동치 판정

두 이진 결정 트리가 같은 불리언 함수를 표현하는지 확률적으로 판정. P인지 알려지지 않은(=효율적 결정 알고리즘의 존재가 불명) 문제를 무작위성으로 해결.

이진 결정 트리란

  • 내부 노드 = 변수, 왼쪽 자식 = 참 할당, 오른쪽 자식 = 거짓 할당
  • 리프 = T 또는 F (함수의 최종 값)
  • 같은 불리언 함수를 여러 트리로 표현 가능 ⇒ 동치 판정이 문제

알고리즘 아이디어

  1. 변수 개수가 n일 때, 소수 p > 2n을 고른다
  2. 각 변수에 \{0, 1, …, 2n-1\}에서 무작위 수 i 할당 (x = True 경우)
  3. x = False는 1 - i \mod p 값으로 할당
  4. 리프가 T인 모든 경로를 찾아, 경로 위 변수에 할당된 값을 곱한다 (mod p)
  5. 모든 T-경로 값을 합한다 (mod p) — 이것이 트리의 "서명"
  6. 두 트리의 서명이 같으면 YES, 다르면 NO
False Positive 오류

두 트리가 다른 함수를 표현하는데 서명이 우연히 같을 가능성. 이것이 이 알고리즘의 한계.

왜 무작위성을 쓰나

이 문제의 결정론적 다항 시간 알고리즘이 존재하는지 아직 모른다. 무작위를 쓰면 효율적으로 풀 수 있고, 오류 확률도 지수적으로 줄일 수 있다 — 실용적 타협.

Part VII

그래프 알고리즘

연결 컴포넌트부터 최단 거리까지. 정리 세션에서 세 알고리즘(Warshall, Floyd, Dijkstra)의 공통 구조를 강조했다.

21 · 연결 컴포넌트

무방향 그래프의 연결 컴포넌트 = 서로 경로로 연결된 최대 노드 집합. Disjoint Set 자료구조로 구현.

Disjoint Set 연산

  • Make-Set(x): 원소 x만 있는 새 집합 생성
  • Find-Set(x): x가 속한 집합의 대표 원소 반환
  • Union(x, y): xy가 속한 집합들을 합침

알고리즘

CONNECTED-COMPONENTS(G)
  for each vertex v ∈ G.V
    MAKE-SET(v)
  for each edge (u,v) ∈ G.E
    if FIND-SET(u) ≠ FIND-SET(v)
      UNION(u, v)

분석

  • Make-Set 연산 수: |V|
  • Find-Set, Union 연산 수: 최대 |E|개 (간선마다 2번 Find + 최대 1번 Union)
  • 효율적 구현(rank + path compression)으로 O((V+E) · α(V)), 사실상 선형
Kruskal과의 차이

Kruskal은 간선을 가중치 순으로 처리하지만, 연결 컴포넌트 찾기는 간선 순서가 무관하다. 둘 다 같은 Disjoint Set 구조를 쓴다.

22 · Warshall 알고리즘 (이행적 폐쇄)

방향 그래프에서 "노드 i에서 j로 가는 양의 길이 경로가 존재하는가?"를 모든 쌍에 대해 결정.

점화식

r^{(k)}[i][j] = "중간 노드로 \{1, …, k\}만 허용했을 때 i → j 경로 존재" (1 또는 0)

$$r^{(k)}[i][j] = r^{(k-1)}[i][j] \lor \left( r^{(k-1)}[i][k] \land r^{(k-1)}[k][j] \right)$$

논리: i → j 경로가 있으려면 둘 중 하나: (1) k를 안 지나는 경로가 이미 있었거나, (2) k를 지나서 i → k → j로 가는 경로.

시간 복잡도: Θ(V³). 연산 구조: \{0,1\}에 대해 AND, OR.

23 · Floyd 알고리즘 (모든 쌍 최단 거리)

Warshall과 논리 구조가 같지만, "경로 존재" 대신 "최단 거리"를 구한다.

점화식

$$d^{(k)}[i][j] = \min \left( d^{(k-1)}[i][j],\ d^{(k-1)}[i][k] + d^{(k-1)}[k][j] \right)$$

논리: i → j 최단 거리는 (1) k를 안 지나는 기존 거리, 또는 (2) k를 지나는 새 경로 중 짧은 쪽.

시간 복잡도: Θ(V³). 연산 구조: 비음 실수 ∪ {0, ∞}에 대해 MIN, PLUS.

24 · Dijkstra 알고리즘 (단일 출발지 최단 거리)

한 출발지에서 모든 노드까지의 최단 거리. 모든 간선 가중치가 비음수여야 작동.

구조

  1. 출발지 s의 거리 D[s] = 0, 나머지
  2. 방문한 노드 집합 S = ∅, min-priority queue에 모든 노드
  3. 큐가 빌 때까지 반복:
    • 가장 작은 D값을 가진 노드 w를 추출
    • S에 추가
    • w의 각 이웃 v에 대해 D[v] 갱신 (relaxation)

시간 복잡도: Min-priority queue 구현에 따라 O((V+E) log V).

세 알고리즘의 공통 구조

Warshall, Floyd, Dijkstra는 (집합, 두 연산) 구조로 통합 이해할 수 있다. Warshall: ({0,1}, AND, OR). Floyd·Dijkstra: (비음 실수∪{∞}, MIN, PLUS). 다른 집합·연산에 같은 알고리즘 골격을 적용하면 다양한 문제가 풀린다.

Part VIII

과제 문제 복기

교수님이 정리 세션에서 직접 "과제를 참고하라"고 언급. 4문제 중 3·4번은 특히 시험에 직결된다.

25 · 과제 Q3 · X(n) 시간 복잡도

문제

X(n)
  r = 0
  for i = 1 to n-1 do
    for j = i+1 to n do
      for k = 1 to j do
        r = r + 1
  return r

X(n)의 return 값을 n에 대한 식으로 구하라.

풀이

단계 1. 가장 안쪽 루프는 k=1…j로 돌아서 j번 실행. 즉 rj가 더해짐.

단계 2. 중간 루프를 정리:

$$\sum_{j=i+1}^{n} j = \sum_{j=1}^{n} j - \sum_{j=1}^{i} j = \frac{n(n+1)}{2} - \frac{i(i+1)}{2}$$

단계 3. 바깥 루프까지 합하면:

$$r = \sum_{i=1}^{n-1} \left[ \frac{n(n+1)}{2} - \frac{i(i+1)}{2} \right]$$ $$= \frac{n(n+1)(n-1)}{2} - \frac{1}{2}\sum_{i=1}^{n-1} i^2 - \frac{1}{2}\sum_{i=1}^{n-1} i$$

단계 4. 공식 대입 (\sum i^2 = m(m+1)(2m+1)/6, \sum i = m(m+1)/2, m = n-1):

$$= \frac{n(n+1)(n-1)}{2} - \frac{(n-1)n(2n-1)}{12} - \frac{n(n-1)}{4}$$

단계 5. n(n-1)/2로 묶기:

$$= \frac{n(n-1)}{2} \left[ n+1 - \frac{2n-1}{6} - \frac{1}{2} \right] = \frac{n(n-1)}{2} \cdot \frac{6n+6-2n+1-3}{6} = \frac{n(n-1)}{2} \cdot \frac{4n+4}{6}$$
최종 답

$$r = \frac{n(n-1)(n+1)}{3} = \Theta(n^3)$$

26 · 과제 Q4 · 점화식 풀이

문제

$$T(1) = 1, \quad T(n) = 3T(n-1) + 2 \text{ for } n \ge 2$$

닫힌 형태로 T(n)을 구하라.

풀이 · 반복 대입

  • T(1) = 1
  • T(2) = 3·T(1) + 2 = 3 + 2 = 3¹ + 3⁰·2
  • T(3) = 3·T(2) + 2 = 3(3+2) + 2 = 3² + (3¹+3⁰)·2
  • T(4) = 3·T(3) + 2 = 3³ + (3²+3¹+3⁰)·2

패턴:

$$T(n) = 3^{n-1} + (3^{n-2} + 3^{n-3} + \cdots + 3^0) \cdot 2$$

등비수열 합 공식:

$$T(n) = 3^{n-1} + \frac{3^{n-1} - 1}{3 - 1} \cdot 2 = 3^{n-1} + (3^{n-1} - 1)$$
최종 답

$$T(n) = 2 \cdot 3^{n-1} - 1$$

27 · 재귀 함수 Trace

2024-2와 2025-1 중간고사에 동일한 문제가 출제됨.

문제

A = [3, 1, 2], n = 3일 때 Y(1)의 실행 결과를 모든 중간 단계와 함께 쓰라.

Y(i)
  if i == n then print A
  else
    Y(i+1)
    for j = i+1 to n
      swap(A[i], A[j])
      Y(i+1)
      swap(A[i], A[j])  // 원복

의도

이 함수는 배열의 모든 순열(permutation)을 생성·출력한다.

실행 Trace

초기 A = [3, 1, 2]

  1. Y(1) 호출. i=1, n=3, 같지 않음.
  2. 먼저 Y(2) 호출 (재귀). 현재 A = [3, 1, 2]
    • Y(2)에서 Y(3) 호출 → i=n, print [3, 1, 2]
    • j=3: swap(A[2], A[3]) → A = [3, 2, 1], Y(3) → print [3, 2, 1], swap 원복 → A = [3, 1, 2]
  3. j=2: swap(A[1], A[2]) → A = [1, 3, 2], Y(2) 호출
    • Y(3) → print [1, 3, 2]
    • j=3: swap(A[2], A[3]) → A = [1, 2, 3], Y(3) → print [1, 2, 3], swap 원복 → A = [1, 3, 2]
    swap 원복 → A = [3, 1, 2]
  4. j=3: swap(A[1], A[3]) → A = [2, 1, 3], Y(2) 호출
    • Y(3) → print [2, 1, 3]
    • j=3: swap(A[2], A[3]) → A = [2, 3, 1], Y(3) → print [2, 3, 1], swap 원복 → A = [2, 1, 3]
    swap 원복 → A = [3, 1, 2]
출력 결과

[3,1,2], [3,2,1], [1,3,2], [1,2,3], [2,1,3], [2,3,1]

3! = 6개 순열

Part IX

시험 당일 체크리스트

D-1, D-day에 빠뜨리지 말아야 할 것들.

마지막 확인

반드시 암기 상태여야 할 것들

  • Matrix chain m[i,j] 테이블 (A₁~A₄, 2×3, 3×4, 4×2, 2×3 → 48)
  • Unit task scheduling이 matroid임의 증명 (hereditary + exchange)
  • Activity selection greedy choice property 증명 (교환 논법)
  • Randomized Quicksort O(n log n) 증명 (X_ij, 2/(j-i+1))
  • Freivalds 오류 증명 (d^T x = 0 → x_1 확률 ≤ 1/2)
  • X(n) 시간복잡도 n(n-1)(n+1)/3
  • T(n) = 3T(n-1) + 2 → 2·3^(n-1) - 1

증명 시 빠지기 쉬운 부분

  • 매트로이드 증명: exchange 성질에서 "|A| > |B|"와 "a ∈ A \ B"를 명시적으로 쓰자
  • Quicksort 분석: "최초로 피벗이 되는 원소" 개념을 설명해야 2/(j-i+1)이 정당화됨
  • Optimal substructure 증명: cut-and-paste 논리 (더 좋은 부분해로 교체 → 모순)을 명시적으로 쓰자
  • Activity selection 증명: "종료시간 순 정렬" 가정과 f₁ ≤ fₖ 조건을 꼭 쓰자

답안 작성 요령

  • 재귀 함수 trace: 중간 단계 하나도 빠뜨리지 말 것. 각 재귀 호출 전·후 배열 상태 기록
  • Matrix chain: 대각선 순서대로 (길이 2 → 3 → 4) 채우면서 k 값과 계산 과정을 모두 쓰기
  • Greedy/DP 증명: "최적해 가정 → 부분해도 최적 주장 → 반대 가정 시 모순" 3단 구조 고수
  • 시간 복잡도: Θ vs O 구별. 상한만 요구받으면 Big-O면 충분
Final Thought

알고리즘 시험은 암기 시험이 아니라 구조를 보는 시험이다. "왜 이 기법이 여기서 작동하는가"를 언어로 설명할 수 있다면, 비슷한 신규 문제가 나와도 풀 수 있다.

· · ·

Good luck on your exam.
The best preparation is understanding, not memorization.