📌 문제 링크 - https://www.acmicpc.net/problem/1202
문제
세계적인 도둑 상덕이는 보석점을 털기로 결심했다.
상덕이가 털 보석점에는 보석이 총 N개 있다. 각 보석은 무게 Mi와 가격 Vi를 가지고 있다. 상덕이는 가방을 K개 가지고 있고, 각 가방에 담을 수 있는 최대 무게는 Ci이다. 가방에는 최대 한 개의 보석만 넣을 수 있다.
상덕이가 훔칠 수 있는 보석의 최대 가격을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000)
다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000)
다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ci ≤ 100,000,000)
모든 숫자는 양의 정수이다.
출력
첫째 줄에 상덕이가 훔칠 수 있는 보석 가격의 합의 최댓값을 출력한다.
힌트 : 두 번째 예제의 경우 첫 번째 보석을 두 번째 가방에, 세 번째 보석을 첫 번째 가방에 넣으면 된다.
풀이
처음에는 단순하게 우선순위큐를 사용하여 보석 가격 기준으로 내림차순, 가방에 담을 수 있는 무게 기준으로 내림차순 해서 제출했었다. 그리고 3%에서 틀렸다..
반례를 찾아보니
9 5
4 5
4 9
4 10
8 55
14 20
9 15
8 55
8 5
11 54
10
5
4
15
20
----
정답 : 183
보석 9개, 가방 5개(20, 15, 10, 5, 4)
이러한 반례가 있었고, 내가 짠 로직대로라면 183이 아닌 144가 나왔다.
그림으로 보면 아래와 같다.
보석은 가격을 기준으로 내림차순, 가방도 무게를 기준으로 내림차순 후 가방에 담을 수 있는 보석이라면 보석 가격을 더해주는 식으로 코드를 짰었다. 때문에 '55 + 55 + 15 + 10 + 9 = 144'라는 결과가 나온 것이다.
하지만 아래처럼 보석을 훔친다면 183이 나온다.
큰 가방은 작은 가방이 담지 못하는 보석도 담을 수 있으니 가방 오름차순, 보석도 무게를 기준으로 오름차순을 했다. 또 가장 비싼 보석을 훔치기 위해 가격을 기준으로 내림차순 한 임시 주머니를 만들어서 현재 가방이 담을 수 있는 보석들을 임시 주머니에 다 넣어주고, 임시 주머니에서 가장 비싼 보석을 꺼내 가격을 더해주는 식으로 변경하였다.
// 임시 주머니 (보석 가격 기준으로 내림차순)
PriorityQueue<Integer> tmp = new PriorityQueue<>(Collections.reverseOrder());
long sum = 0; // 훔칠 수 있는 보석의 최대 가격
int bag;
while(!bags.isEmpty()) {
bag = bags.poll(); // 현재 가방 무게
// 임시 주머니에 현재 가방에 담을 수 있는 보석 다 담아
while(!jewels.isEmpty() && jewels.peek().M <= bag) {
tmp.offer(jewels.poll().V);
}//while
if(!tmp.isEmpty()) sum += tmp.poll(); // 보석 무게 더해주기
}//while
return sum;
위의 그림처럼 가방4가 담을 수 있는 보석 3개를 임시 주머니에 넣은 후 그중 제일 비싼 보석인 1번 보석을 꺼내 10원을 더해준다. 그 후 남아있는 보석 중(무게 8부터 14)에 가방5가 가질 수 있는 보석은 없으므로 가방5는 임시 주머니에서 가장 비싼 보석인 2번 보석을 꺼내 9원을 더해준다. 가방10은 보석 무게가 8부터 9까지 다 가능하므로 임시 주머니에 담아준 후 임시 주머니에서 가장 비싼 보석인 5번 보석을 꺼내 55를 더해주게 된다. 이렇게 가방20까지 반복해서 최종적으로 183원이 나오게 된다.
코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
// https://www.acmicpc.net/problem/1202
public class Main_1202 {
static PriorityQueue<Jewel> jewels; // 보석
static PriorityQueue<Integer> bags; // 가방
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken()); // 보석 개수
int K = Integer.parseInt(st.nextToken()); // 가방 개수
jewels = new PriorityQueue<>(); // 보석들 (보석 무게 기준으로 오름차순)
while(N-->0) {
st = new StringTokenizer(br.readLine());
int M = Integer.parseInt(st.nextToken());
int V = Integer.parseInt(st.nextToken());
jewels.offer(new Jewel(M, V));
}//while
bags = new PriorityQueue<>(); // 가방들(가방 무게 기준으로 오름차순)
while(K-->0) {
bags.offer(Integer.parseInt(br.readLine()));
}//while
br.close();
long max = getMax();
System.out.print(max);
}//main
private static long getMax() {
// 임시 주머니 (보석 가격 기준으로 내림차순)
PriorityQueue<Integer> tmp = new PriorityQueue<>(Collections.reverseOrder());
long sum = 0; // 훔칠 수 있는 보석의 최대 가격
int bag;
while(!bags.isEmpty()) {
bag = bags.poll(); // 현재 가방 무게
// 임시 주머니에 현재 가방에 담을 수 있는 보석 다 담아
while(!jewels.isEmpty() && jewels.peek().M <= bag) {
tmp.offer(jewels.poll().V); // 보석 가격 담기
}//while
if(!tmp.isEmpty()) sum += tmp.poll(); // 보석 가격 더해주기
}//while
return sum;
}//getMax
static class Jewel implements Comparable<Jewel> {
int M; // 보석 무게
int V; // 보석 가격
public Jewel(int M, int V) {
this.M = M;
this.V = V;
}
@Override
public int compareTo(Jewel j) {
return this.M - j.M; // 보석 무게 오름차순
}
}//Jewel
}//class
'Algorithm > 백준' 카테고리의 다른 글
[Java] 백준 3745. 오름세 (0) | 2025.01.03 |
---|---|
[Java] 백준 6209. 제자리 멀리뛰기 (0) | 2025.01.02 |
[Java] 백준 2611. 자동차경주 (1) | 2024.12.23 |
[Java] 백준 19645. 햄최몇? (0) | 2024.12.16 |
[Java] 백준 9997. 폰트 (0) | 2024.12.05 |