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

비트마스킹 (Bitmask)

· 수정 · 📖 약 2분 · 767자/단어 #algorithm #foundation #bitmask
Bit Manipulation, bitmask, 비트마스킹, 비트 마스크, bit-mask

정의

비트마스킹 (Bitmask)집합 / 상태정수의 비트 패턴 으로 인코딩해 집합 연산을 비트 연산으로 처리하는 기법.

{0, 2, 3} 같은 집합을 0b1101 = 13 으로 표현하면, 멤버 확인 / 추가 / 제거 / 합집합 / 교집합 모두 &, |, ^, ~ 한 줄.

크기 N ≤ 20 (또는 25) 인 작은 집합의 모든 부분집합 순회 + DP 의 정형. Bitfield DP, SOS DP 의 기반.

문제 상황과 동기

“N 명을 선택하는 방법은 2^N 개. N=20 이면 약 10^6. 모든 부분집합을 보고 점수 최대 / 최소를 구하라”

  • naive set / vector: 한 부분집합당 메모리 / 시간 O(N). 총 O(N · 2^N) = 2·10^7. 빡빡.
  • bitmask: 한 부분집합을 int 하나 로. O(1) operations. 총 O(2^N) ~ O(N · 2^N).

핵심 통찰: 원소 N 이 32 이하면, 한 부분집합 = 한 정수 = 한 캐시 라인 안에서 모든 연산.

시각화

핵심 아이디어

각 원소를 비트 위치 에 대응. 집합 S 의 비트 표현:

S = {0, 2, 3, 5}  →  mask = (1<<0) | (1<<2) | (1<<3) | (1<<5) = 0b101101 = 45

핵심 연산

의미연산
원소 iS 에 있나?(mask >> i) & 1
원소 i 추가mask | (1 << i)
원소 i 제거mask & ~(1 << i)
원소 i 토글mask ^ (1 << i)
|S| (popcount)__builtin_popcount(mask)
합집합mask1 | mask2
교집합mask1 & mask2
차집합mask1 & ~mask2
대칭차mask1 ^ mask2
부분집합?(mask1 & mask2) == mask1

모든 부분집합 순회

for mask = 0..(1<<N)-1:
    // mask = 한 부분집합

부분집합의 부분집합 순회 (SOS 의 기반)

sub = mask
while sub > 0:
    // sub = mask 의 부분집합 중 하나
    sub = (sub - 1) & mask
// sub = 0 한번 더 처리

총 시간 O(3^N) (각 원소가 in/out/없음 3 상태).

알고리즘

// 모든 부분집합에서 어떤 값을 최대화
best = 0
for mask = 0..(1<<N)-1:
    score = 0
    for i = 0..N-1:
        if (mask >> i) & 1:
            score += value[i]
    best = max(best, score)

구현

// N 개 아이템 중 무게합 ≤ W 인 부분집합의 최대 가치합 (N ≤ 20)
#include <bits/stdc++.h>
using namespace std;
int main() {
  int n, W; cin >> n >> W;
  vector<int> w(n), v(n);
  for (auto& x : w) cin >> x;
  for (auto& x : v) cin >> x;
  int best = 0;
  for (int mask = 0; mask < (1 << n); mask++) {
      int sw = 0, sv = 0;
      for (int i = 0; i < n; i++) if ((mask >> i) & 1) {
          sw += w[i]; sv += v[i];
      }
      if (sw <= W) best = max(best, sv);
  }
  cout << best << "\n";
}
stdin
4 10
2 3 4 5
3 4 5 6
결과
13

(N=4 아이템, 무게합 ≤ 10. 최적: {0,1,2,3} 다 못 담으니 {0,2,3} 무게 11 안 됨, {0,1,3} 무게 10, 가치 13)

복잡도

작업비용
모든 부분집합 순회O(2^N)
각 부분집합의 부분집합O(3^N) (모든 mask 누적)
단일 비트 연산O(1)
popcountO(1) (__builtin_popcountll)
N 의 한계20~25 정도까지 실용. 그 이상은 Subset Sum / DP 필요

변형 / 활용

패턴예시
**[[DP Bitfield비트필드 DP]]**
**[[SOS DPSum over Subsets DP]]**
FWHT비트 연산 컨볼루션
State compression작은 그리드 / 자릿수 상태
Bitset Optimization큰 비트 배열 (N > 64)

함정

1. 32-bit overflow

N = 32 이상이면 1 << N 이 int overflow. 1LL << N 사용 (long long).

2. 음수 shift

1 << 31 의 결과는 signed int 의 부호 비트, undefined-ish 행동. unsigned 또는 long long 사용.

3. popcount 가 느린 환경

Java < 5 또는 일부 임베디드 환경에서는 Integer.bitCount 가 SW 구현. PS 에서는 보통 hardware popcnt.

4. 부분집합 순회의 중첩

for mask in 0..(1<<N)-1 안에서 또 for i in 0..N-1 로 비트 검사하면 O(N · 2^N). 이걸 막으려면 popcount + table 또는 SOS 변환.

표준 라이브러리

언어비트 카운트비트 마스크
C++__builtin_popcountll, __builtin_ctzll, std::popcount (C++20)std::bitset<N> (큰 N)
Pythonbin(x).count('1'), x.bit_count() (3.10+)내장 int 자체
JavaInteger.bitCount, Long.bitCountBitSet
Rustu32::count_onesbit-vec crate

BOJ 연습 문제

번호제목정답률링크
BOJ 11723집합-kokoa-lab
BOJ 1182부분수열의 합-kokoa-lab
BOJ 2098외판원 순회-kokoa-lab
BOJ 1052물병-kokoa-lab

참고

이 글의 용어 (6개)
비트마스크 DP (Bitmask DP)algorithm
정의 비트마스크 DP (Bitmask DP) 는 상태 공간이 부분집합 으로 표현될 때, 각 부분집합을 정수의 비트로 인코딩해 DP 상태로 삼는 기법. N ≤ 20 범위에서 O(2…
Bitset Optimizationalgorithm
정의 Bitset Optimization 은 불리언 / 비트 단위 정보 를 또는 배열에 패킹해, 한 명령어로 64 비트씩 병렬 연산 함으로써 시간 복잡도를 /64 (또는 , w …
DP on Bitmask: 비트마스크 DPalgorithm
정의 부분집합 상태를 비트마스크로 인코딩 하여 DP 를 수행. 원소 수 N ≤ 20 정도에서 유효. 자세한 내용은 DP Bitfield 참조. 대표 예 TSP = 방문한 도시 m…
FWHT (Fast Walsh-Hadamard Transform)algorithm
정의 FWHT (Fast Walsh-Hadamard Transform) 은 비트 연산 (XOR, AND, OR) 에 대한 컨볼루션 을 O(N log N) 에 계산하는 변환. FF…
SOS DP (Sum Over Subsets)algorithm
정의 SOS DP (Sum Over Subsets) 는 길이 2^N 의 배열 a 에 대해, 모든 mask (0..2^N-1) 마다 mask 의 부분 집합 (submask) 에 대…
Subset Sum: 부분집합 합algorithm
정의 집합 {a1, ..., an} 에서 부분집합의 원소 합이 특정 값 T 가 되는 것이 존재하는가. NP-complete in general, but pseudo-polynom…

💬 댓글

사이트 검색 / 명령어

검색

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