본문으로 건너뛰기
김신건의 로그

Hamiltonian Path: 모든 정점 한 번씩

· 수정 · 📖 약 2분 · 604자/단어 #algorithm #graph #hamiltonian #np-hard
Hamiltonian Path, 해밀턴 경로, Hamiltonian Cycle

정의

Hamiltonian Path 는 그래프의 모든 정점을 정확히 한 번씩 방문하는 경로. 시작 = 끝이면 Hamiltonian Cycle.

일반 그래프에서 NP-complete. Euler path (모든 간선을 한 번씩) 와 대비: Euler 는 다항 시간(Hierholzer 알고리즘).

문제 상황

N개의 정점과 M개의 간선으로 이루어진 그래프에서 모든 정점을 정확히 한 번씩 방문하는 경로가 존재하는가?

naive (백트래킹): 모든 정점 순열을 시도. O(N! x N). N=15 이상이면 불가능.

핵심 통찰: “방문한 정점 집합” 자체를 상태로 만들어 DP. dp[mask][v]를 “방문 집합 = mask, 현재 v에 있을 때 도달 가능 여부”로 정의. 상태 수 O(2^N x N), 전이 O(N).

시각화

Bitmask DP 상태 전이 (N=4, 정점 0에서 출발)

flowchart LR
    S["출발: 정점 0만 방문"]
    A["정점 0,1 방문, 현재 위치 1"]
    B["정점 0,2 방문, 현재 위치 2"]
    C["정점 0,1,2 방문, 현재 위치 2"]
    D["정점 0,1,3 방문, 현재 위치 3"]
    E["정점 0,1,2,3 방문 완료"]
    S -->|"0에서 1 이동"| A
    S -->|"0에서 2 이동"| B
    A -->|"1에서 2 이동"| C
    A -->|"1에서 3 이동"| D
    C -->|"2에서 3 이동"| E
    D -->|"3에서 2 이동"| E

Bitmask 표현 (N=4)

mask (이진)십진방문 정점 집합
000110
00113{0, 1}
01015{0, 2}
01117{0, 1, 2}
111115{0, 1, 2, 3} (전체)

핵심 아이디어

상태: dp[mask][v] = “방문 집합 = mask, 현재 v에 있는 상태가 도달 가능한가.”

초기: dp[1 << s][s] = true (출발 정점 s 하나만 방문).

전이: 현재 상태 (mask, u)에서 미방문 정점 v로 이동.

if dp[mask][u] and edge(u,v) and v not in mask:
    dp[mask | (1 << v)][v] = true

: full = (1 << N) - 1인 전체 집합을 포함하는 mask에서 도달 가능한 v가 존재하면 YES.

  • Hamiltonian Path: any(dp[full][v] for v in 0..N-1) → 경로만 확인
  • Hamiltonian Cycle: any(dp[full][v] and edge(v, start) for v in 0..N-1) → 시작점 복귀

비트 연산 정리

연산코드
v 방문 여부 확인mask & (1 << v)
v 추가mask | (1 << v)
v 제거mask & ~(1 << v)
전체 집합(1 << N) - 1

알고리즘

# Hamiltonian Path Bitmask DP
# 단일 출발점 s 기준

dp[1 << s][s] = true
for mask = 1 to (1<<N) - 1:
    for u = 0 to N-1:
        if not dp[mask][u]: continue
        if not (mask & (1 << u)): continue   # 유효 상태 체크
        for v in adj[u]:
            if mask & (1 << v): continue      # 이미 방문
            dp[mask | (1 << v)][v] = true

full = (1 << N) - 1
answer = any(dp[full][v] for v in 0..N-1)

구현

#include <bits/stdc++.h>
using namespace std;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  int n, m;
  cin >> n >> m;

  vector<vector<int>> adj(n);
  for (int i = 0; i < m; i++) {
      int u, v;
      cin >> u >> v;
      adj[u].push_back(v);
      adj[v].push_back(u);  // 무방향 그래프
  }

  // dp[mask][v]: mask 집합 방문 후 v에 위치 가능한가
  int FULL = (1 << n) - 1;
  vector<vector<bool>> dp(1 << n, vector<bool>(n, false));

  // 모든 정점을 출발점으로 시도
  for (int s = 0; s < n; s++)
      dp[1 << s][s] = true;

  for (int mask = 1; mask <= FULL; mask++) {
      for (int u = 0; u < n; u++) {
          if (!dp[mask][u]) continue;
          if (!(mask & (1 << u))) continue;
          for (int v : adj[u]) {
              if (mask & (1 << v)) continue;  // 이미 방문
              dp[mask | (1 << v)][v] = true;
          }
      }
  }

  bool found = false;
  for (int v = 0; v < n; v++)
      if (dp[FULL][v]) { found = true; break; }

  cout << (found ? "YES" : "NO") << "\n";
  return 0;
}
stdin
4 5
0 1
1 2
2 3
0 2
1 3
결과
YES

복잡도

항목
시간O(2^N x N^2)
공간O(2^N x N)
N=152^15 x 225 ~ 7.4M (여유)
N=202^20 x 400 ~ 420M (타이트)
실용 한계N <= 20

NOTE

N=20 기준 메모리: bool dp[2^20][20] = 약 20MB. int 사용 시 80MB.

함정

1. 유효 상태 체크 누락

if (!(mask & (1 << u))) continue;
// 이 체크가 없으면 u가 mask에 없는 잘못된 상태에서 전이 발생

2. 출발 정점 범위

Hamiltonian Path는 어떤 정점에서도 시작 가능. 모든 s에 대해 dp[1<<s][s]=true로 초기화. 시작점을 0으로 고정하면 방향 있는 경우에만 유효.

3. 방향 vs 무방향

  • 무방향: adj[u].push_back(v)adj[v].push_back(u) 둘 다
  • 방향: 단방향만. Hamiltonian Cycle 존재 조건이 달라짐.

4. Cycle vs Path 혼동

Cycle: 마지막 정점 v에서 출발점 s로 돌아올 수 있는 간선 존재 확인 필요.

// Hamiltonian Cycle 확인 (출발 s 고정 시)
for (int v = 0; v < n; v++)
    if (dp[FULL][v] && adj_matrix[v][s]) found = true;

5. 백트래킹 vs DP

N <= 12 정도는 백트래킹도 빠름. N=15~20에서 DP가 확실히 유리. N > 20은 모두 불가.

BOJ 연습 문제

번호제목난이도알고리즘
BOJ 1194달이 차오른다, 가자.Gold 1BFS + bitmask
BOJ 2098외판원 순회Gold 1TSP, bitmask DP
BOJ 1987알파벳Gold 4백트래킹
BOJ 17471게리맨더링Gold 4부분집합 + bitmask

참고

이 글의 용어 (3개)
비트마스크 DP (Bitmask DP)algorithm
정의 비트마스크 DP (Bitmask DP) 는 상태 공간이 부분집합 으로 표현될 때, 각 부분집합을 정수의 비트로 인코딩해 DP 상태로 삼는 기법. N ≤ 20 범위에서 O(2…
DP on Bitmask: 비트마스크 DPalgorithm
정의 부분집합 상태를 비트마스크로 인코딩하여 DP를 수행하는 기법. 정수 의 i번째 비트가 1이면 "원소 i 선택", 0이면 "미선택"을 의미한다. 원소 수 N ≤ 20 정도의 …
TSP (Traveling Salesman Problem): 외판원 순회algorithm
정의 N 개 도시를 정확히 한 번씩 방문 후 시작점으로 돌아오는 최소 비용 경로. NP-hard. 공식 표현: N 개 정점의 완전 그래프에서 Hamiltonian Cycle 중 …

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기