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

단조 큐 최적화 (Monotone Queue Optimization)

· 수정 · 📖 약 2분 · 731자/단어 #algorithm #dp #optimization #deque
monotone queue optimization, 단조 큐 최적화, monotone-queue-optimization

정의

단조 큐 최적화 (Monotone Queue Optimization) 는 DP 전이 dp[i] = min/max_{j ∈ window(i)} f(j) + g(i) 에서, 전이 후보 j 의 집합슬라이딩 윈도우 를 이루고 f(j) 가 결합된 값이 단조 일 때, monotonic deque 로 후보 집합을 O(1) amortized 에 유지해 DP 전체를 O(N) 으로 가속하는 기법.

슬라이딩 윈도우최댓값/최솟값 구하기 기본 패턴이 그대로 DP 에 들어간다. Deque Trick 의 일반화.

문제 상황과 동기

DP 전이가 다음과 같다고 하자.

dp[i] = max_{j ∈ [i - K, i - 1]} (dp[j] + value[i])
  • naive: 매 i 마다 윈도우 K 개 후보 다 봄. O(N · K).
  • monotone deque: 후보 deque 에 유효한 j 만 유지. 한 j 가 deque 에 한 번 push, 한 번 pop. O(N) amortized.

핵심 통찰: dp[j_1] ≤ dp[j_2] 이고 j_1 < j_2 이면 j_1 은 영원히 무용지물. 새로 들어오는 i 에 대해 j_2 가 항상 후보로 더 좋다 (또는 같다). 그래서 deque 에는 DP 값이 단조 감소 인 인덱스만 남긴다.

시각화

핵심 아이디어

deque 안의 인덱스: j_1 < j_2 < ... < j_k
이들의 DP 값: dp[j_1] > dp[j_2] > ... > dp[j_k]   (max 최적화)

새 인덱스 i 추가:
  while deque.back() 의 dp 값 ≤ dp[i]: pop_back
  push_back(i)

윈도우 밖 인덱스 제거:
  while deque.front() < i - K: pop_front

답:
  dp[i] = dp[deque.front()] + value[i]

각 인덱스가 deque 에 한 번 push, 한 번 pop. 총 O(N).

알고리즘

monotone_queue_dp(N, K, value):
    dp[0] = 0
    deque = [0]
    for i = 1..N:
        # 윈도우 밖 제거
        while deque.front() < i - K:
            deque.pop_front()
        # 답
        dp[i] = dp[deque.front()] + value[i]
        # 새 후보 추가 (단조 유지)
        while deque is not empty and dp[deque.back()] ≤ dp[i]:
            deque.pop_back()
        deque.push_back(i)
    return dp[N]

구현

// 길이 K 윈도우의 max DP, O(N)
#include <bits/stdc++.h>
using namespace std;
int main() {
  int n, k; cin >> n >> k;
  vector<long long> v(n + 1), dp(n + 1, 0);
  for (int i = 1; i <= n; i++) cin >> v[i];
  deque<int> dq;
  dq.push_back(0);
  for (int i = 1; i <= n; i++) {
      while (!dq.empty() && dq.front() < i - k) dq.pop_front();
      dp[i] = dp[dq.front()] + v[i];
      while (!dq.empty() && dp[dq.back()] <= dp[i]) dq.pop_back();
      dq.push_back(i);
  }
  cout << dp[n] << "\n";
}
stdin
5 2
3 1 4 1 5
결과
13

(5 칸, 한 번에 최대 2 칸 점프. 각 칸의 값을 더해 최대화. 답 = 3+4+1+5 = 13)

복잡도

항목
시간O(N) amortized
공간O(N) (DP + deque)
윈도우 크기K (변동 가능, 동적 K 도 OK)

각 원소가 deque 에 한 번 push, 한 번 pop → 총 O(N).

변형 / 활용

패턴응용
윈도우 최댓값sliding window max
K 점프 DP1 ~ K 칸 도약 게임
CHT선형식 DP 의 envelope (단조성 조건 추가)
회전 윈도우circular 배열의 윈도우

함정

1. 같은 값의 비교

dp[deque.back()] ≤ dp[i]dp[deque.back()] < dp[i] 의 차이가 답에 영향. 같은 값 도 pop 하는 것이 일반적.

2. 윈도우 정의

[i - K, i - 1][i - K + 1, i] 의 인덱싱 차이. 문제 정의에 맞춰 front 제거 조건 을 정확히.

3. min 과 max 의 대칭

max 최적화는 단조 감소 deque, min 최적화는 단조 증가 deque. 부등호 방향 헷갈리지 않게.

4. 음수 / 0 처리

value[i] 가 음수면 작은 j 가 더 좋을 수도. DP 정의를 다시 점검.

CHT 와의 관계

Convex Hull Trick선형식 m·x + b 들의 lower/upper envelope. monotone queue 는 일반적인 어떤 함수 값 의 monotone 후보. CHT 는 monotone queue 의 추가 기하 구조 + 이분 탐색 변형.

조건기법
후보가 일반 값monotone queue
후보가 직선 + 쿼리가 단조CHT (deque)
후보가 직선 + 쿼리 임의CHT (Li-Chao tree)

BOJ 연습 문제

번호제목정답률링크
BOJ 11003최솟값 찾기-kokoa-lab
BOJ 5977Mowing the Lawn-kokoa-lab
BOJ 17400깃발춤-kokoa-lab

참고

이 글의 용어 (5개)
덱 활용 구간 최댓값 트릭 (Monotonic Deque Trick)algorithm
정의 덱 트릭 (Deque Trick) 은 덱 (double-ended queue) 에 원소의 인덱스를 단조로운 값 순서 로 유지해, 슬라이딩 윈도우의 최댓값 또는 최솟값을 O(…
분할정복 DP 최적화 (Divide and Conquer Optimization)algorithm
정의 분할정복 DP 최적화 (Divide and Conquer Optimization) 는 꼴의 DP 에서, 최적 분할점 opt[i][j] 가 j 에 대해 단조 비감소 ( ) 일…
슬라이딩 윈도우 (Sliding Window)algorithm
정의 슬라이딩 윈도우 (Sliding Window) 는 배열/문자열 위에서 고정 크기 또는 가변 크기 윈도우가 한 방향으로 미끄러지며 각 윈도우마다 조건 (합, 최댓값, 중복 여…
크누스 최적화 (Knuth Optimization)algorithm
정의 크누스 최적화 (Knuth Optimization) 는 구간 DP 에서 최적 분할점 k = opt[i][j] 의 단조성 을 이용해 시간 복잡도를 O(N^3) 에서 O(N^2…
CHT (Convex Hull Trick)algorithm
정의 Convex Hull Trick (CHT) 은 DP 전이 같이 선형식들의 lower envelope (또는 upper envelope) 에서 한 점 값을 평가하는 패턴을, …

💬 댓글

사이트 검색 / 명령어

검색

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