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

애드 혹 (Ad Hoc)

· 수정 · 📖 약 3분 · 1,094자/단어 #algorithm #foundation #ad-hoc
ad hoc, 애드 혹, 임기응변

정의

애드 혹 (Ad Hoc) 은 범용 알고리즘이 아닌, 그 문제만의 관찰·트릭·창의적 사고로 푸는 문제 유형. 라틴어로 “이 목적을 위한” 이라는 뜻.

정형화된 자료구조나 DP 점화식 없이, 문제를 꼼꼼히 읽고 패턴·대칭·패리티 같은 수학적 성질을 발견하는 것이 핵심.

문제 상황과 동기

알려진 알고리즘 (BFS, DP, 세그트리 등) 이 명확히 적용되지 않는다.

  • naive: 단순 시뮬레이션, 브루트포스, O(2^N) 또는 O(N^3) 이상.
  • ad hoc: 문제 안의 숨은 성질 (패리티, 주기, 대칭, 경우 분류) 을 찾아 O(1) ~ O(N) 으로 해결.

핵심 통찰: 문제 자체가 힌트. 제약, 입력 범위, 샘플 입출력에서 규칙성을 귀납.

자주 등장하는 PS 위치:

  • Codeforces Div2 A~C (관찰 위주)
  • IOI, ICPC 의 창의적 문제
  • 프로토타이핑, 해커톤 (시간 제약 아래 빠른 아이디어 검증)

시각화

핵심 아이디어

invariant: 문제 조건 자체가 해법의 힌트.

대표 관찰 기법:

  1. 패리티 (parity): 홀짝 구분. 체스판 색칠, 불변량.
  2. 대칭 (symmetry): 좌우/상하 대칭, 순환 대칭.
  3. 극값 (extremal): 최댓값/최솟값이 정답인 경우.
  4. 경우 분류 (case work): if-else 로 모든 경우를 나눔 (→ 케이스 워크 참고).
  5. 주기 (periodicity): 반복 패턴 (예: 1,2,3,1,2,3,…).
  6. 그리디 (greedy): 증명 없이도 직관적으로 최적.

알고리즘

1. 문제 읽기 (2~3회 정독, 제약·샘플 주목)
2. 샘플 입출력 손으로 시뮬레이션
3. 패턴 / 불변량 찾기 (패리티, 대칭, 극값, ...)
4. 가설 세우기 → 반례 찾기
5. 반례 없으면 증명 (또는 "AC 받으면 증명" → submit)
6. 구현 (보통 짧고 단순)

구현

예제: 체스판 색칠 (패리티 관찰)

8×8 체스판에서 (r1, c1) 에서 (r2, c2) 로 나이트(Knight) 이동. 흑백 칸이 번갈아 나오는가?

관찰: 나이트 이동은 항상 다른 색으로 이동. (r + c) % 2 가 플립.

// 체스판 색칠: (r+c) % 2
#include <bits/stdc++.h>
using namespace std;
int main() {
  int r1, c1, r2, c2;
  cin >> r1 >> c1 >> r2 >> c2;
  // 나이트 한 번에 다른 색으로
  if ((r1 + c1) % 2 == (r2 + c2) % 2) {
      cout << "same color, need even steps\n";
  } else {
      cout << "different color, need odd steps\n";
  }
}
stdin
1 1 2 3
결과
same color, need even steps

예제: 구슬 뒤집기 (대칭 관찰)

N개 구슬이 일렬. 좌우 대칭이면 “YES”, 아니면 “NO”.

관찰: a[i] == a[N-1-i] for all i.

// 좌우 대칭 체크, O(N)
#include <bits/stdc++.h>
using namespace std;
int main() {
  int n; cin >> n;
  vector<int> a(n);
  for (auto& v : a) cin >> v;
  bool ok = true;
  for (int i = 0; i < n / 2; i++) {
      if (a[i] != a[n - 1 - i]) ok = false;
  }
  cout << (ok ? "YES" : "NO") << "\n";
}
stdin
5
1 2 3 2 1
결과
YES

예제: 수열 주기 찾기 (periodicity)

수열 1, 2, 3, 1, 2, 3, ... 에서 k번째 수는?

관찰: 주기 3. ans = ((k - 1) % 3) + 1.

// 주기 3, O(1)
#include <bits/stdc++.h>
using namespace std;
int main() {
  int k; cin >> k;
  cout << ((k - 1) % 3) + 1 << "\n";
}
stdin
7
결과
1

복잡도

항목
시간문제마다 다름, 보통 O(1)~O(N)
공간문제마다 다름, 보통 O(1)~O(N)
구현 난이도낮음 (관찰만 맞으면 코드 짧음)

변형

기법설명
패리티 불변량XOR, mod 2 연산으로 상태 추적
경우 분류if-else 나무, 모든 케이스 커버 (→ [[case-work
주기 찾기시뮬레이션 후 규칙 귀납, mod 연산
대칭 활용좌우/상하 대칭, 회전 대칭
극값 관찰max, min, gcd, lcm 이 정답인 경우
그리디 휴리스틱증명 없이 직관으로 시도

함정

1. 반례 간과

관찰이 틀렸는데도 샘플만 맞아서 AC 로 착각. 엣지 케이스 (N=1, 음수, 0, 최댓값) 수동 검증 필수.

2. 과도한 일반화

“N≤10 인데 DP?” → 오버엔지니어링. ad hoc 은 단순한 if-else 로 충분한 경우가 많다.

3. 증명 없이 제출

관찰이 맞는지 손으로 반례 3~5개 찾아보고 제출. Codeforces 는 강한 프리테스트 덕분에 괜찮지만, 대회에선 WA 패널티.

4. 문제 잘못 읽기

제약 오해 (1-indexed vs 0-indexed, inclusive vs exclusive) 가 ad hoc 에서 치명적. 2~3회 정독.

BOJ 연습 문제

번호제목정답률링크
BOJ 1000A+B-kokoa-lab
BOJ 10817세 수-kokoa-lab
BOJ 2920음계-kokoa-lab
BOJ 10951A+B - 4 (EOF)-kokoa-lab
BOJ 12128진수 2진수-kokoa-lab

참고

이 글의 용어 (3개)
그리디 (Greedy)algorithm
정의 그리디 (Greedy) 알고리즘은 매 단계마다 국소 최적 (locally optimal) 선택을 하여 전역 최적해 (globally optimal solution) 에 도달…
케이스 워크 (Case Work)algorithm
정의 케이스 워크 (Case Work) 는 문제를 여러 조건/경우로 분류하여 각각 해결하는 패턴. if-else, switch-case, 의사결정 트리 등으로 구현. 복잡한 조건…
해 구성하기 (Constructive)algorithm
정의 해 구성하기 (Constructive Algorithm) 는 답의 존재를 증명하는 대신, 명시적으로 답을 만드는 문제 유형. "가능한가?" → YES/NO 가 아니라, "가…

💬 댓글

사이트 검색 / 명령어

검색

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