어떤 문제를 푸는가
"어떤 원소들을 이미 사용했는가" 같은 부분집합 상태 가 DP의 차원이 되어야
할 때, 그 집합을 정수의 비트로 압축해 상태로 삼는 기법입니다. 원소 수가 작을
때(\(n \le 20\) 정도)만 가능하며, 대표 문제가 외판원 문제(TSP) 입니다.
외판원 문제로 보는 핵심
\(n\)개 도시를 모두 한 번씩 방문하고 출발 도시로 돌아오는 최소 비용 순회 를
구합니다. 단순 완전 탐색은 \(O(n!)\)이라 \(n=12\)만 돼도 불가능합니다.
상태를 이렇게 둡니다.
$$ dp[mask][i] = \text{집합 } mask \text{의 도시를 모두 방문했고 현재 } i \text{에 있을 때 최소 비용} $$
mask의 \(i\)번째 비트는 켜져 있어야 합니다(지금 \(i\)에 있으니 \(i\)는 방문됨).
점화식
\(i\)에 도달하기 직전 정점 \(j\)에서 왔다고 보면,
$$ dp[mask][i] = \min_{j \in mask,\ j \ne i} \bigl( dp[mask \setminus \{i\}][j] + cost[j][i] \bigr) $$
"\(i\)를 빼낸 집합을 \(j\)까지 방문하고, \(j \to i\)로 이동"한 비용입니다.
기저는 출발 도시 \(s\)만 방문한 상태 \(dp[\{s\}][s] = 0\). 답은 모든 도시를
방문한 뒤 출발지로 돌아오는 비용
$$ \min_{i} dp[(1{\ll}n) - 1][i] + cost[i][s] $$
왜 옳은가
순회의 마지막 두 정점만 보면, "\(i\)로 끝나는 부분 경로의 최소 비용"은 그 직전
정점 \(j\)를 무엇으로 골랐는지에만 의존하고, 어떤 순서로 \(mask\)를 방문했는지는
무관 합니다. 같은 (방문 집합, 현재 위치)면 미래 비용이 동일하므로, 이
요약이 충분한 상태가 됩니다. 이것이 \(n!\)을 \(2^n \cdot n\)으로 압축하는 근거입니다.
복잡도
상태 수 \(2^n \cdot n\), 각 전이 \(O(n)\)이므로
$$ O(2^n \cdot n^2) $$
\(n = 16\)이면 약 \(1.7 \times 10^7\)로 충분, \(n = 20\)이면 \(4 \times 10^8\)로
빡빡합니다. \(n\)이 20을 넘으면 비트마스크 DP는 보통 무리 입니다.
어떤 문제가 이 형태인가
- "모든 것을 한 번씩" + "작은 원소 수" → 방문 집합을 마스크로.
- "짝짓기/배정" + "작은 한쪽 크기" → 한쪽의 사용 집합을 마스크로.
- 격자 채우기에서 "한 줄의 칸 사용 패턴"을 마스크로(브로큰 프로파일).
다음 강의에서 TSP 구현과 메모리/순회 디테일을 봅니다.