문제 설명
각 행에 3개의 값이 담긴 작업(task) 행렬이 하나 있고, 또 다른 값 k가 주어진다고 가정해 봅시다. 우리는 tasks에서 k개의 행을 선택해 이를 S라고 부르며, 다음 합이 최소가 되도록 선택한 뒤 그 결과를 반환해야 합니다.
max(S[0, 0], S[1, 0], ..., S[k-1, 0]) + max(S[0, 1], S[1, 1], ..., S[k-1, 1]) + max(S[0, 2], S[1, 2], ..., S[k-1, 2])
쉽게 말해, 3개의 열 각각이 비용에 기여하며 그 값은 S 안에서 해당 열의 최댓값으로 계산됩니다. 단, 빈 리스트의 최댓값은 0으로 간주합니다.
예시
예를 들어 입력이 다음과 같다고 해보겠습니다.
tasks = [[2, 3, 3], [4, 5, 2], [4, 2, 3]], k = 2
이때 출력은 10이 됩니다. 첫 번째 행과 마지막 행을 선택하면 S = [[2, 3, 3], [4, 2, 3]]이 되고, 각 열의 최댓값은 다음과 같습니다.
- max(S[0, 0], S[1, 0]) = 4
- max(S[0, 1], S[1, 1]) = 3
- max(S[0, 2], S[1, 2]) = 3
따라서 전체 합은 4 + 3 + 3 = 10이 됩니다.
풀이 접근 방법
이 문제는 정렬과 최소 힙(min-heap)을 함께 활용하면 효율적으로 해결할 수 있습니다. 단계별로 살펴보겠습니다.
util() 함수
- util() 함수를 정의합니다. 이 함수는 리스트 B를 인자로 받습니다.
- B를 정렬합니다.
- yheap := 범위 0 ~ K-1의 각 i에 대해 -B[i, 1] 값을 담은 리스트로 초기화합니다.
- yheap을 힙 구조로 만듭니다(heapify).
- ans := B[K - 1, 0] + (-yheap[0])으로 초기화합니다.
- i를 K부터 B의 크기까지 반복합니다.
- x := B[i, 0]
- yheap에 -B[i, 1]을 삽입하되 힙의 크기를 K로 유지합니다(heappushpop).
- y := -yheap[0]
- ans := ans와 x + y 중 더 작은 값
- ans를 반환합니다.
메인 로직
- A가 비어 있거나 K가 0이면 0을 반환합니다.
- A를 정렬합니다.
- B := 범위 0 ~ K-1의 각 i에 대해 [A[i, 1], A[i, 2]] 쌍으로 이루어진 리스트를 만듭니다.
- ans := A[K - 1, 0] + B에 있는 y들의 최댓값 + B에 있는 z들의 최댓값으로 초기화합니다.
- i를 K부터 A의 크기까지 반복합니다.
- B에 [A[i][1], A[i][2]]를 추가합니다.
- ans := ans와 A[i, 0] + util(B) 중 더 작은 값
- ans를 반환합니다.
동작 원리 요약
핵심 아이디어는 다음과 같습니다. 먼저 작업을 첫 번째 열 값을 기준으로 오름차순 정렬하면, 특정 행을 기준점으로 삼았을 때 그 행의 첫 번째 열 값이 선택된 행들 중 최댓값이 됩니다. 이후 나머지 두 열에 대해서는 힙을 활용해 후보군 안에서 가장 작은 '최댓값의 합' 조합을 효율적으로 탐색합니다. 이를 통해 모든 조합을 일일이 확인하는 브루트포스 방식보다 훨씬 빠르게 최적해를 구할 수 있습니다.
구현 예제
다음 구현을 통해 더 잘 이해해 보겠습니다.
import heapq
class Solution:
def solve(self, A, K):
if not A or not K:
return 0
def util(B):
B.sort()
yheap = [-B[i][1] for i in range(K)]
heapq.heapify(yheap)
ans = B[K - 1][0] + (-yheap[0])
for i in range(K, len(B)):
x = B[i][0]
heapq.heappushpop(yheap, -B[i][1])
assert len(yheap) == K
y = -yheap[0]
ans = min(ans, x + y)
return ans
A.sort()
B = [[A[i][1], A[i][2]] for i in range(K)]
ans = A[K - 1][0] + max(y for y, z in B) + max(z for y, z in B)
for i in range(K, len(A)):
B.append([A[i][1], A[i][2]])
ans = min(ans, A[i][0] + util(B))
return ans
ob = Solution()
tasks = [
[2, 3, 3],
[4, 5, 2],
[4, 2, 3]
]
k = 2
print(ob.solve(tasks, k))입력
tasks = [ [2, 3, 3], [4, 5, 2], [4, 2, 3] ] k = 2
출력
10