[백준] 1781번 - 컵라면

동현·2022년 10월 17일
0
post-thumbnail

1. 문제

상욱 조교는 동호에게 N개의 문제를 주고서, 각각의 문제를 풀었을 때 컵라면을 몇 개 줄 것인지 제시 하였다. 하지만 동호의 찌를듯한 자신감에 소심한 상욱 조교는 각각의 문제에 대해 데드라인을 정하였다.

문제 번호1234 567
데드라인1133226
컵라면 수6721451

위와 같은 상황에서 동호가 2, 6, 3, 1, 7, 5, 4 순으로 숙제를 한다면 2, 6, 3, 7번 문제를 시간 내에 풀어 총 15개의 컵라면을 받을 수 있다.

문제는 동호가 받을 수 있는 최대 컵라면 수를 구하는 것이다. 위의 예에서는 15가 최대이다.

문제를 푸는데는 단위 시간 1이 걸리며, 각 문제의 데드라인은 N이하의 자연수이다. 또, 각 문제를 풀 때 받을 수 있는 컵라면 수와 최대로 받을 수 있는 컵라면 수는 모두 231보다 작거나 같은 자연수이다.

2. 입력

첫 줄에 숙제의 개수 N (1 ≤ N ≤ 200,000)이 들어온다. 다음 줄부터 N+1번째 줄까지 i+1번째 줄에 i번째 문제에 대한 데드라인과 풀면 받을 수 있는 컵라면 수가 공백으로 구분되어 입력된다.

3. 출력

첫 줄에 동호가 받을 수 있는 최대 컵라면 수를 출력한다.

4. 예제 입력 / 출력

7
1 6
1 7
3 2
3 1
2 4
2 5
6 1

15

5. 풀이 과정

데드라인 안에 가장 많은 컵라면을 얻기 위해 골라야할 문제는 아래와 같다.

  1. 데드라인이 작은 문제
  2. 같은 데드라인이여도 얻을 수 있는 컵라면이 많은 문제

문제를 풀 때마다 단위 시간이 1이 걸리므로 푼 문제 수는 곧 걸린 시간과 같다. 많은 문제를 풀기 위해서는 데드라인이 작은 문제를 먼저 푸는 것이 유리하다.
그렇다고 무턱대고 데드라인이 작은 문제 먼저 푸는 것이 유리한 것은 아니다. 다음 문제를 예시로 들어보자.

문제 번호123
데드라인122
컵라면 수276

데드라인이 작은 문제 먼저 푼다면 1, 2번을 풀어 총 컵라면을 9개 획득하겠지만 2, 3번을 푼다면 컵라면을 총 13개 획득할 수 있다.

이 문제의 핵심은 선택하지 않는 것이 더 최적인 경우를 고려하는 것이다.

해결방법은 다음과 같다.

  1. 데드라인은 오름차순, 컵라면 개수는 내림차순으로 문제를 정렬한다.
  2. 문제를 차례대로 탐색
  3. 현재 문제의 데드 라인이 푼 문제 수 (걸린 시간) 보다 크다면 문제를 풀 수 있는 경우
  4. 반대로 현재 문제의 데드 라인이 푼 문제 수 (걸린 시간) 이하라면 문제를 풀 수 없는 경우
  5. 현재 문제를 풀 수 있는 경우 -> 현재 문제를 푼 것으로 취급
    현재 문제를 풀 수 없는 경우 -> 가장 적은 컵라면을 주는 문제 대신 현재 문제를 푸는 것이 이득이라면 현재 문제를 푼 것으로 취급
import java.io.*
import java.util.*

fun main() = with(BufferedReader(InputStreamReader(System.`in`))) {
    val n = readLine().toInt()
    // 우선 순위 큐 구현 (데드라인↓, 컵라면↑)
    val pq = PriorityQueue { a: Pair<Int, Int>, b: Pair<Int, Int> -> 
    	when {
            a.first != b.first -> a.first.compareTo(b.first)
            else -> b.second.compareTo(a.second)
        }
    }

    for (i in 0 until n) {
        var (deadline, ramen) = readLine().split(' ').map{ it.toInt() }
        pq.add(Pair(deadline, ramen))
    }

    // 푼 문제들을 담는 우선순위 큐 (최소 힙)
    val pq2 = PriorityQueue<Int> { a, b -> a.compareTo(b) }

    while (pq.isNotEmpty()) {
        var temp = pq.poll()
        // 현재 문제의 데드라인이 푼 문제 수 보다 클 경우 (현재 문제를 풀 수 있는 경우)
        if (pq2.size < temp.first) {
            pq2.add(temp.second)
        } else {
            // 푼 문제 수가 현재 문제의 데드라인 이하라면 (현재 문제를 풀 수 없는 경우)
            // 가장 적게 라면을 준 문제보다 현재 문제를 푸는 것이 이득인지 고려
            if (temp.second > pq2.peek()) {
                pq2.poll()
                pq2.add(temp.second)
            }
        }
    }

    var answer: Long = 0
    while (pq2.isNotEmpty()) {
        answer += pq2.poll()
    }
    println(answer)
}

데드라인을 오름차순, 컵라면 개수를 내림차순으로 정렬하기 위한 우선순위큐, 가장 적은 컵라면을 주는 문제를 반환하기 쉽도록 우선순위큐를 하나 만들어 사용하였다.

6. 문제 링크

https://www.acmicpc.net/problem/1781

profile
https://github.com/DongChyeon

0개의 댓글