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

브루트포스 (Bruteforcing)

· 수정 · 📖 약 3분 · 1,156자/단어 #algorithm #foundation #bruteforcing
bruteforce, 브루트포스, 완전 탐색, exhaustive search

정의

브루트포스 (Bruteforcing, 완전 탐색) 는 가능한 모든 경우를 체계적으로 나열하고 검증하는 알고리즘. 프로그래밍 대회에서 가장 기초이자 가장 안전한 접근이며, 문제 크기가 충분히 작을 때 최적해를 보장한다.

대표 기법: 순열 (permutation), 조합 (combination), 부분집합 (subset, 비트마스크), 재귀적 백트래킹.

문제 상황과 동기

N 명의 사람을 배치하거나, M 개 중 K 개를 고르거나, 2^N 개 부분집합 중 조건을 만족하는 것을 찾아야 한다.

  • naive (잘못된 접근): 단일 for 루프로 전부 시도하려 하면 구조 복잡.
  • brute force (올바른 접근): 조합론 기법 (next_permutation, bitmask) 또는 재귀로 모든 경우를 명시적으로 생성. 탐색 범위가 10^6 이하면 대부분 가능.

핵심 통찰: 모든 가능성을 다 보면 절대 놓치지 않는다. 최적화가 불명확할 때는 일단 완전 탐색으로 정답을 찾고, 시간 내에 들어오면 그대로 제출.

PS 에서 “N ≤ 10” 같은 제약은 브루트포스 신호.

시각화

핵심 아이디어

가능한 모든 후보를 생성하고 각 후보를 검증.

candidates = generate_all()
answer = null
for c in candidates:
    if is_valid(c):
        if is_better(c, answer):
            answer = c

invariant: 지금까지 본 후보 중 최적해를 유지. 전부 보면 전역 최적.

대표 구현 패턴:

  • 순열 (permutation): STL next_permutation 또는 swap 재귀. O(N!).
  • 조합 (combination): DFS 또는 C(N, K) 생성. O(N choose K).
  • 부분집합 (subset): 비트마스크 0..2^N-1 또는 DFS. O(2^N).
  • 백트래킹 (backtracking): 조건 위반시 조기 종료해 상수 감소. 최악 복잡도는 동일하지만 평균 케이스 훨씬 빠름.

알고리즘

비트마스크로 부분집합 열거

for mask = 0 to 2^N - 1:
    subset = []
    for i = 0 to N-1:
        if mask & (1 << i):
            subset.append(a[i])
    if is_valid(subset):
        update_answer(subset)

재귀로 조합 생성 (C(N, K))

def choose(idx, chosen):
    if len(chosen) == K:
        process(chosen)
        return
    if idx == N:
        return
    choose(idx+1, chosen + [a[idx]])   # 선택
    choose(idx+1, chosen)               # 건너뜀

구현

// 비트마스크로 2^N 부분집합 열거
#include <bits/stdc++.h>
using namespace std;
int main() {
  int n; cin >> n;
  vector<int> a(n);
  for (auto& v : a) cin >> v;
  int max_sum = -1e9;
  for (int mask = 0; mask < (1 << n); mask++) {
      int sum = 0;
      for (int i = 0; i < n; i++) {
          if (mask & (1 << i))
              sum += a[i];
      }
      max_sum = max(max_sum, sum);
  }
  cout << max_sum << "\n";
}
stdin
4
1 -2 3 4
결과
8

복잡도

항목
시간 (순열)O(N!)
시간 (조합 C(N,K))O(N choose K)
시간 (부분집합)O(2^N · T_check)
공간O(N) 재귀 스택 또는 O(1) 반복
안정성-

T_check = 한 후보 검증 비용. 부분집합 합이면 O(N), 순열 조건이면 O(N^2) 등.

실전 분기점

N경우의 수1초 제한 가능?비고
N ≤ 10≤ 10! ≈ 3.6M순열 직접 열거 가능
N ≤ 20≤ 2^20 ≈ 1M부분집합 열거 가능
N ≤ 25≤ 2^25 ≈ 33M✓ (타이트)비트마스크 한계
N ≤ 402^40 ≈ 1.1T[[Meet In The Middle
N ≤ 12, K=2≤ C(12,2)=66조합 열거

백트래킹 가지치기가 강하면 N=15 순열도 가능할 수 있음 (상수 큰 폭 감소).

변형

1. next_permutation (C++ STL)

sort(a.begin(), a.end());
do {
    // process permutation a
} while (next_permutation(a.begin(), a.end()));

O(N!) 번 순열 생성. 사전순 순서 보장.

2. 백트래킹 (Backtracking)

조건 위반 시 더 이상 탐색하지 않음. N-Queen 같은 제약 만족 문제.

void bt(int depth, vector<int>& chosen) {
    if (pruning_condition()) return;  // 가지치기
    if (depth == target) {
        process(chosen);
        return;
    }
    for (int i = 0; i < N; i++) {
        chosen.push_back(i);
        bt(depth + 1, chosen);
        chosen.pop_back();
    }
}

3. 중간에서 만나기

N=40 부분집합을 앞 20 / 뒤 20 으로 나눠 각각 2^20, 합쳐서 O(2^20 log 2^20).

함정

1. 중복 순열 vs 조합

“3개 중 2개를 뽑는다” 가 순서를 고려하면 P(3,2)=6, 순서 무관이면 C(3,2)=3. 문제 조건 정확히 읽기.

2. 비트마스크 범위 초과

1 << 30 은 int 범위 내 (2^30 ≈ 1e9), but 1 << 31 은 부호 비트. 1LL << i 또는 unsigned 사용.

3. 시간 초과 낙관

“N=20 이면 2^20=1M 여유있다” 가 아니라, 내부 루프 비용까지 곱해야. 부분집합 합 문제면 O(2^N · N), N=20 이면 20M, 타이트. N=25 면 800M, 아슬아슬하거나 TLE.

4. 공간 초과

모든 후보를 메모리에 저장하면 N! / 2^N 개 배열. 거의 항상 불가능. 생성과 동시에 검증/업데이트.

BOJ 연습 문제

번호제목정답률링크
BOJ 2798블랙잭48.2%kokoa-lab
BOJ 1018체스판 다시 칠하기45.1%kokoa-lab
BOJ 14888연산자 끼워넣기49.3%kokoa-lab
BOJ 14889스타트와 링크45.7%kokoa-lab
BOJ 1182부분수열의 합42.8%kokoa-lab

참고

이 글의 용어 (4개)
백트래킹 (Backtracking)algorithm
정의 백트래킹 (Backtracking) 은 DFS + 가지치기 (pruning) 로 모든 경우를 탐색하되, 불가능한 후보를 조기 제거하여 탐색 공간을 극적으로 줄이는 알고리즘.…
비트마스킹 (Bitmask)algorithm
정의 비트마스킹 (Bitmask) 은 집합 / 상태 를 정수의 비트 패턴 으로 인코딩해 집합 연산을 비트 연산으로 처리하는 기법. 같은 집합을 으로 표현하면, 멤버 확인 / 추가…
재귀 (Recursion)algorithm
정의 재귀 (Recursion) 는 함수가 자기 자신을 호출하는 프로그래밍 기법. base case (종료 조건) 와 recursive case (재귀 단계) 로 구성되며, 복잡…
MITM (Meet in the Middle)algorithm
정의 Meet in the Middle (MITM) 은 입력을 절반으로 나누어 각각의 모든 경우를 2^(N/2) 에 열거한 뒤, 두 결과를 합쳐서 정답을 찾는 알고리즘. naiv…

💬 댓글

사이트 검색 / 명령어

검색

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