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

덱 DP (슬라이딩 윈도우 최적화)

· 수정 · 📖 약 2분 · 603자/단어 #algorithm #dp #dp-deque #optimization
deque DP, monotonic queue DP, sliding window DP, 덱 DP, 모노토닉 큐 DP

정의

덱 DP (Deque DP) 는 점화식 dp[i] = min/max_{j in [i-k, i-1]} (dp[j] + cost(i,j)) 꼴에서 monotonic deque 를 써서 최적 j 후보를 O(N) 에 유지하는 기법. 슬라이딩 윈도우 최댓값/최솟값의 일반화로, 윈도우 내에서 비용까지 포함한 최적 인덱스 를 관리한다.

문제 상황과 동기

dp[i] = max(dp[j] + a[j]) for j < i and i - j <= K. Naive 하면 각 i 에서 K 개 순회 -> O(NK). N=10^6, K=10^5 면 10^11.

핵심 통찰: 윈도우 안의 후보 중 dp[j] + something 이 최대인 j 만 deque 에 유지. 더 이상 최적이 될 수 없는 후보는 pop.

시각화

핵심 아이디어

DP: dp[i] = best(dp[j] + a[i])  for j in [i-K, i-1]
    where best = maximal computed value

Deque invariant:
  1. Index range: front is always within [i-K, i-1]
  2. Value order: dp[deque[0]] >= dp[deque[1]] >= ... (decreasing)
  3. Old index from front when out of range
  4. New candidate deletes from back if inferior

Deque 에는 인덱스 만 저장. dp[deque.front()] 이 항상 현재 i 에 대한 최적값.

알고리즘

deque dq
for i = 0..N-1:
    # 1. 만료된 front 제거
    while dq not empty and dq.front() < i - K:
        dq.pop_front()

    # 2. 현재 dp[i] 계산
    if dq empty:
        dp[i] = a[i]
    else:
        dp[i] = dp[dq.front()] + a[i]

    # 3. dq.back() 이 dp[i] 보다 나쁘면 제거
    while dq not empty and dp[dq.back()] <= dp[i]:
        dq.pop_back()

    # 4. 현재 i 를 후보에 추가
    dq.push_back(i)

구현

// Maximum sum subsequence: pick elements at distance <= K
// dp[i] = max sum ending at i, each pair distance ≤ K
#include <bits/stdc++.h>
using namespace std;
int main() {
  int n, k; cin >> n >> k;
  vector<int> a(n);
  for (int i = 0; i < n; i++) cin >> a[i];
  vector<long long> dp(n);
  deque<int> dq;
  long long ans = 0;
  for (int i = 0; i < n; i++) {
      while (!dq.empty() && dq.front() < i - k)
          dq.pop_front();
      dp[i] = a[i];
      if (!dq.empty()) dp[i] += dp[dq.front()];
      while (!dq.empty() && dp[dq.back()] <= dp[i])
          dq.pop_back();
      dq.push_back(i);
      ans = max(ans, dp[i]);
  }
  cout << ans << "\n";
  return 0;
}
stdin
8 3
1 -3 5 -2 8 -1 4 -6
결과
13

복잡도

항목
시간 (최선/평균/최악)O(N)
공간O(N) (dp 배열) or O(K) (deque + rolling)
각 원소의 deque push/popamortized O(1)

변형 / 활용

변형설명
Sliding window minimum부등호 반대 (≤ -> >=). dp 없이 순수 배열 최솟값
DP + sliding window costdp[i] = min(dp[j] + cost(i,j)) 에서 cost 가 분해 가능할 때
Divide and Conquer DPmonotone queue 와 결합, dp[i][j]
1D/1D DP점화식 차수가 1차원, 최적화 상수가 K 일 때

널리 사용되는 예: BOJ 11003 (최솟값 찾기), BOJ 2096 (내려가기, 슬라이딩 윈도우 DP).

함정

1. 부등호 방향

최댓값: dp[dq.back()] <= dp[i] 이면 pop. 최솟값: >= 로 반대. 혼동하지 않도록.

2. 초기 K 값

i < K 일 때는 윈도우가 충분히 크지 않음. i - k 가 음수가 되지 않도록 조건.

3. 0-1 범위 반영

dp[i] 가 기본값 a[i] (단독 선택) 인지, 아니면 이전 dp 값과 합쳐야 하는지. 문제 정의에 따라.

BOJ 연습 문제

번호제목정답률링크
BOJ 11003최솟값 찾기-kokoa-lab
BOJ 2096내려가기-kokoa-lab
BOJ 14865골목길-kokoa-lab

참고

이 글의 용어 (4개)
동적 계획법 (Dynamic Programming)algorithm
정의 동적 계획법 (Dynamic Programming, DP) 은 큰 문제를 작은 부분 문제로 나누고, 각 부분 문제의 최적해를 저장하여 중복 계산을 제거하는 최적화 기법. R…
두 포인터 (Two Pointer)algorithm
정의 두 포인터 (Two Pointer) 는 정렬 또는 단조 구조 에서 두 인덱스 , 을 서로 다른 속도 / 방향 으로 이동시키며 부분 구간 / 페어를 O(N) 에 탐색하는 기법…
세그먼트 트리 (Segment Tree)algorithm
정의 세그먼트 트리 (Segment Tree) 는 배열의 구간 쿼리 (range query) 와 점 갱신 (point update) 를 모두 O(log N) 에 처리하는 이진 트…
슬라이딩 윈도우 (Sliding Window)algorithm
정의 슬라이딩 윈도우 (Sliding Window) 는 배열/문자열 위에서 고정 크기 또는 가변 크기 윈도우가 한 방향으로 미끄러지며 각 윈도우마다 조건 (합, 최댓값, 중복 여…

💬 댓글

사이트 검색 / 명령어

검색

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