야근 n시간 동안 작업량을 줄여 “야근 피로도(제곱합)”를 최소화하는 문제
작업량 배열 works가 주어질 때, 매 시간마다 작업량 1을 줄일 수 있고(0 미만 불가), 최종 피로도 (sum works[i]^2)가 최소가 되도록 만든다.
입력
n : 남은 야근 시간(총 n번 -1을 할 수 있음)
works[] : 각 작업의 남은 작업량
예를 들어,
n = 4
works = [4, 3, 3]
매 시간 “어느 작업을 1 줄일지” 선택해서 최종 제곱합을 최소화한다.
출력
야근을 n시간 진행한 후, 남은 작업량들의 제곱합(야근 지수)을 반환한다.
import java.util.*;
public class A02힙정렬문제풀이 {
// 야근지수
// 그리디(탐욕법) : 지금 당장봤을 때 맞겠는데?싶으면 그리디다 (이게 뭔 소리임)
public static long solution12927(int n, int[] works) {
// n: 앞으로 야근할 수 있는 총 시간(= 총 n번의 “-1 작업”을 할 수 있음).
// works[i]: i번째 작업의 남은 작업량(정수).
// 1시간에 할 수 있는 일: works 중 하나를 골라서 1만큼 감소시키는 것(0 밑으로는 내려가면 안 됨).
// 이 때 works 중 가장 큰 값을 빼서 -1 하고 넣어주고, 또 가장 큰 값을 빼서 -1해서 넣어주고... 할 수 있는 시간만큼 다 균등하게 빼줌
long answer = 0;
Queue<Integer> pq = new PriorityQueue<>(Comparator.reverseOrder()); // 최대힙
int totalWork = 0;
for (int i = 0; i < works.length; i++) {
pq.add(works[i]);
totalWork += works[i];
}
if (totalWork < n) {
return 0;
}
for (int i = 0; i < n; i++) {
int change = pq.peek() - 1;
pq.poll();
pq.add(change);
}
while (!pq.isEmpty()) {
answer += (long) pq.peek() * pq.poll();
}
return answer;
}
}
if (totalWork < n) return 0;
works.length번 삽입즉 “힙 연산을 n번 반복”하는 형태라, n이 크면 반복 비용이 핵심이 된다(그래서 PQ 사용 이유가 명확해진다).
입력
n = 4
works = [4, 3, 3]
아이디어:
| ❌ 실수 | ✅ 해결 |
|---|---|
| 총 작업량이 n보다 작은데도 계속 처리 | totalWork < n이면 바로 0 리턴 |
| 최소힙을 써서 매번 최소를 줄임 | 피로도를 줄이려면 보통 “최대부터 감소” 방향이 필요 |
| 매번 정렬해서 최대를 찾음 | 최대힙(PQ)으로 “최대 꺼내기/갱신” 패턴 고정 |
| 감소 후 0 미만 가능성 고려 안 함 | 문제 조건상 0 미만 불가(총합 체크로 방지) |