데드라인 스케줄링 — 이익을 최대로 만드는 그리디 배치

마감 기한이 정해진 여러 일이 있고, 각각을 끝내면 이익을 얻는다. 시간은 한정되어 모든 일을 할 수는 없다. 어떤 일을 골라 어떤 순서로 해야 이익의 합이 최대가 될까?

이 포스트에서 다루는 내용
  • 데드라인 스케줄링 문제의 정의: 마감 기한과 이익을 가진 일들의 배치
  • 그리디 전략: 이익이 큰 일부터, 마감 기한에 가장 가까운 자리에 넣기
  • 정확성 증명: 교환 논증(exchange argument)으로 최적성 보이기
  • 성능: 단순한 O(N2)O(N^2)에서 균형 트리로 O(NlogN)O(N \log N)까지

데드라인 스케줄링 문제

NN개의 할 일 J1,,JNJ_1, \cdots, J_N 이 있다. 각각의 일은 두 값으로 이루어진다.

Ji=(Di,Pi)J_i = (D_i, P_i)
  • DiD_i : 마감 기한(deadline) — 이 시각까지 끝내야 이익을 얻는다.
  • PiP_i : 이익(profit) — 마감 안에 끝냈을 때 얻는 값. Pi>0P_i > 0 을 전제한다.

모든 일은 각각 1시간이 걸린다. 한 시간에 하나의 일만 할 수 있으므로, 시간 슬롯 1, 2, 3, …에 일을 하나씩 채워 넣는 셈이다. 목표는 마감을 지키며 처리한 일들의 이익 합을 최대로 만드는 것이다.

예시

할 일이 {(2,2),(1,3),(1,1)}\{(2, 2), (1, 3), (1, 1)\} 라고 하자.

  • (1,3)(1, 3)(1,1)(1, 1) 은 둘 다 마감이 1이라, 시간 1 슬롯을 두고 경쟁한다. 둘 중 하나만 할 수 있다.
  • (2,2)(2, 2) 는 마감이 2이므로 시간 2 슬롯에 넣을 수 있다.

가장 이익이 큰 (1,3)(1, 3) 을 시간 1에, (2,2)(2, 2) 를 시간 2에 넣으면 이익은 3+2=53 + 2 = 5. 남은 (1,1)(1, 1) 은 시간 1이 이미 차서 버린다. 이 배치가 최선이다.

이익이 양수라는 전제가 하는 일

Pi>0P_i > 0 은 편의를 위한 조건이 아니라 아래 증명이 실제로 쓰는 전제다. 두 방향으로 새어 나간다.

Pi=0P_i = 0 을 허용하면 최적해에서 빈 슬롯을 발견했을 때 “그 자리에 일을 넣으면 이익이 늘어나므로 최적이 아니다”라는 모순 논법이 막힌다. 이익이 그대로일 수 있기 때문이다. 다만 결론은 살아남는다. 이익이 00 인 일을 넣어도 총 이익이 줄지 않으므로 그 최적해는 여전히 최적이고, 알고리즘의 배치와 맞춰지기만 한다. 논법이 “모순”에서 “손해 없는 추가”로 바뀔 뿐이다.

Pi<0P_i < 0 을 허용하면 알고리즘 자체가 틀린다. 선택 규칙이 “놓을 수 있으면 놓는다”이므로 손해를 보는 일까지 배치한다. 이 경우에는 ”Pi0P_i \le 0 인 일은 아예 보지 않는다”는 규칙을 앞에 붙여야 한다.


그리디 전략

먼저 문제를 단순하게 만드는 세 가지 가정을 세운다. 모두 일반성을 잃지 않는다.

가정이유
모든 마감 기한은 NN 이하다마감을 앞으로 당겨도 문제가 없고, NN 뒤의 일은 어차피 할 시간이 없어 의미가 없다
이익이 내림차순으로 주어진다정렬되어 있지 않다면 정렬하면 된다. 어차피 이익이 큰 것부터 볼 것이다
최적 스케줄에서 모든 일은 마감을 지킨다마감을 어긴 일은 이익이 0이므로, 굳이 넣을 이유가 없다

이제 선택 규칙은 이렇다.

  1. 이익이 큰 일부터 차례로 본다. 나중에 작은 일이 들어와서 못 하게 되더라도 큰 손해가 아니다.
  2. Ji=(Di,Pi)J_i = (D_i, P_i) 를 마감 전에 놓을 수 있다면, 마감 기한에 최대한 가까운 빈 자리에 넣는다. 앞쪽 자리를 비워 두어야, 마감이 임박한 일이 나중에 들어올 자리가 남는다.
예시 일정 {(2,2),(1,3),(1,1)}에서 이익 큰 순서로 슬롯을 채우는 그리디 배치 — (1,3)→슬롯1, (2,2)→슬롯2, (1,1)은 버려짐
예시 일정 {(2,2),(1,3),(1,1)}에서 이익 큰 순서로 슬롯을 채우는 그리디 배치 — (1,3)→슬롯1, (2,2)→슬롯2, (1,1)은 버려짐

위 예시에 규칙을 적용하면, 이익 순서 (1,3)(2,2)(1,1)(1,3) \to (2,2) \to (1,1) 로 본다. (1,3)(1,3) 은 마감 1이라 슬롯 1에, (2,2)(2,2) 는 마감 2라 슬롯 2에 들어간다. 마지막 (1,1)(1,1) 은 마감 1인데 슬롯 1이 이미 차 있어 버려진다. 결과는 이익 5로 최적과 일치한다.

직관은 그럴듯하다. 하지만 그리디 알고리즘에서 보았듯, 매 단계의 국소 최적이 전역 최적을 보장하지는 않는다. 이 전략이 정말 최적인지 증명이 필요하다.


정확성 증명

증명의 핵심 도구는 교환 논증(exchange argument) 이다. “우리 알고리즘의 부분 답을, 어떤 최적해로 손해 없이 맞춰 갈 수 있다”는 것을 단계마다 보인다.

불변식

알고리즘을 실행해 일들이 배치된 상태를 AA 라 하자. 다음을 불변식(invariant)으로 둔다.

AAJiJ_i 까지 본 시점에, 이에 대응하는 최적해 SS 가 적어도 하나 존재한다. 그리고 jij \le i 인 모든 jj 에 대해,

  • JjJ_jAA 에 나타난다     \iff JjJ_jSS 에 나타난다.
  • 나타난다면, JjJ_j위치(시간 슬롯)도 AASS 에서 같다.

이 불변식이 마지막(i=Ni = N)까지 유지되면, AA 자체가 최적해와 완전히 일치하므로 AA 가 최적이다.

기초

i=0i = 0 일 때는 아무 일도 배치하지 않은 상태다. j0j \le 0jj 가 없으므로 조건은 공허하게 참(vacuously true) 이다. 임의의 최적해를 SS 로 잡으면 된다.

귀납 단계

ii 까지 불변식이 참이라 가정하고, Ji+1J_{i+1} 을 처리한 뒤에도 참임을 보인다. AiA_iSSjij \le i 범위에서 일치한다.

경우 1: Ji+1J_{i+1} 을 놓을 수 없는 경우. 마감 Di+1D_{i+1} 전에 빈 자리가 없어 알고리즘이 Ji+1J_{i+1} 을 버린 경우다. AiA_iSSDi+1D_{i+1} 이하 구간에서 동일하므로, SS 에도 빈 자리가 없다. 따라서 SS 역시 Ji+1J_{i+1} 을 넣을 수 없고, 둘 다 Ji+1J_{i+1} 을 갖지 않아 불변식이 유지된다.

경우 2: Ji+1J_{i+1} 을 시간 tt 에 놓는 경우. 알고리즘이 Ji+1J_{i+1} 을 슬롯 tt 에 넣었다. 이번에는 SSJi+1J_{i+1} 을 이미 갖고 있는지를 먼저 나눈다. 이렇게 나누면 두 경우가 겹치지 않는다.

2-1. SSJi+1J_{i+1} 을 이미 갖고 있는 경우. Ji+1J_{i+1}SS 의 슬롯 tt' 에 있다고 하자. tt'tt 의 관계로 나뉜다.

  • t=tt' = t 이면 위치가 같으므로 그대로 일치한다.
  • t>tt' > t 는 불가능하다. 알고리즘이 Ji+1J_{i+1}tt 에 놓았다는 것은, ttDi+1D_{i+1} 사이 슬롯이 이미 모두 차 있었다는 뜻이다. SS 도 그 구간이 동일하게 차 있으므로 t>tt' > tJi+1J_{i+1} 을 둘 수 없다.
  • t<tt' < t 이면 Ji+1J_{i+1} 을 슬롯 tt 로 옮긴다. 슬롯 tt 가 비어 있으면 그대로 옮기고, 다른 일 JxJ_x 가 있으면 Ji+1J_{i+1}JxJ_x 의 자리를 맞바꾼다. 어느 쪽이든 JxJ_x 는 더 앞선 슬롯으로 이동하므로 마감을 어기지 않고, 자리 이동·교환이라 SS 의 총 이익은 변하지 않는다. 옮긴 뒤 SS 는 슬롯 ttJi+1J_{i+1} 을 갖는다.

2-2. SSJi+1J_{i+1} 을 갖고 있지 않은 경우. 이제 SS 의 슬롯 tt 상태만 따지면 된다.

  • SStt 가 비어 있다면, SSJi+1J_{i+1} 을 추가하기만 해도 이익이 Pi+1P_{i+1} 만큼 늘어난다. 전제에서 Pi+1>0P_{i+1} > 0 이므로 SS 가 최적이라는 가정에 모순이고, 따라서 이 경우는 일어나지 않는다.
  • SStt 에 다른 일 JxJ_x 가 있다면, 먼저 x>i+1x > i+1 이다. 만약 x<i+1x < i+1 이었다면 JxJ_x 는 이미 AiA_i 에도 같은 자리에 있었을 텐데, 그러면 알고리즘이 슬롯 ttJi+1J_{i+1} 을 넣지 못했을 것이기 때문이다. x>i+1x > i+1 이므로 이익은 내림차순 가정에 의해 PxPi+1P_x \le P_{i+1}. 따라서 SS 에서 JxJ_xJi+1J_{i+1} 로 바꾸면 이익이 같거나 커진다. SS 가 최적이므로 더 커질 수는 없어 Px=Pi+1P_x = P_{i+1} 이고, 교환해도 이익이 같으니 SS 는 여전히 최적이며 이제 슬롯 ttJi+1J_{i+1} 을 갖는다.

모든 경우에서, Ji+1J_{i+1} 까지 본 상태 Ai+1A_{i+1} 에 일치하는 최적해 SS 를 만들 수 있다. 불변식은 i+1i+1 에서도 성립한다. \blacksquare

증명의 뼈대

"알고리즘의 부분 답 AiA_i 에 일치하는 최적해 SS 가 늘 존재한다"를 불변식으로 두고, 새 일 Ji+1J_{i+1} 을 처리할 때마다 SS손해 없이 교환·이동Ai+1A_{i+1} 에 맞춘다. 이익 내림차순 가정 덕분에 "SS 의 일을 우리 일로 바꿔도 이익이 줄지 않는다"가 보장되는 것이 핵심이다.


성능

같은 알고리즘도 자료 구조에 따라 속도가 크게 달라진다.

단순한 구현: O(N2)O(N^2)

일을 이익 순으로 본 뒤, 각 일마다 마감 DiD_i 에서 시작해 슬롯 1까지 한 칸씩 내려가며 빈 슬롯을 찾는다.

  • 일이 NN 개.
  • 일 하나당 빈 슬롯 탐색이 최악 O(N)O(N).

곱하면 O(N2)O(N^2) 이다.

균형 트리: O(NlogN)O(N \log N)

빈 슬롯 찾기를 균형 이진 트리(balanced tree) 로 가속한다.

  • 사용 가능한 시간 슬롯(또는 마감 후보)을 모두 트리에 넣는다.
  • JiJ_i 를 볼 때마다 트리에 ”DiD_i 이하의 값 중 가장 큰 것은?” 을 질의한다. 이때 트리 안에서 DiD_i 의 순위를 알 수 있고, DiD_i 이하에서 가장 큰 빈 슬롯을 O(logN)O(\log N) 에 찾는다.
  • 그 슬롯에 일을 배치했으면 트리에서 지우고 재배열한다. 삭제·재배열도 O(logN)O(\log N).

NN 개 각각에 대해 O(logN)O(\log N) 작업을 하므로 전체 O(NlogN)O(N \log N). 정렬 비용 O(NlogN)O(N \log N) 과 합쳐도 차수는 그대로다.

핵심 정리
  • 데드라인 스케줄링은 마감 기한 DiD_i 와 이익 PiP_i 를 가진 1시간짜리 일들을, 마감을 지키며 이익 합이 최대가 되도록 시간 슬롯에 배치하는 문제다.
  • 그리디 전략은 이익이 큰 일부터 보고, 각 일을 마감 기한에 최대한 가까운 빈 슬롯에 넣는다. 앞 자리를 비워 두어 임박한 일의 자리를 남긴다.
  • 최적성은 교환 논증으로 증명한다. "부분 답에 일치하는 최적해가 늘 존재한다"는 불변식을, 매 단계 손해 없는 교환으로 유지한다. 이익 내림차순 가정이 교환의 안전을 보장한다.
  • 구현은 단순하면 O(N2)O(N^2) 이지만, 균형 트리로 빈 슬롯 질의·삭제를 O(logN)O(\log N) 에 처리하면 전체 O(NlogN)O(N \log N) 까지 줄어든다.
다음 포스트

구간 스케줄링 — 겹치지 않게 가장 많은 일 고르기 — 이번엔 시작·종료 시간이 있는 구간 일들을 다룬다. 이익이 모두 같을 때, 겹치지 않게 가장 많은 일을 고르는 그리디 전략과 그 최적성을 교환 논증으로 증명한다.

© 2026 XsQuare01. Powered by GitHub Pages. · 방문자