백준 1202 - 보석 도둑
Updated:
Java
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)
모든 숫자는 양의 정수이다.
접근 방법
문제의 조건을 보면, N과 K가 최대 300000이다.
즉 O(N * K)이면 시간초과가 날 가능성이 크며, 보석의 정보가 하나당 최대 1,000,000이므로 결과는 long이 되어야 한다.
문제의 핵심은, 한번 확인한 보석은 다시 돌아보지 않는 다는 것이다.
- 보석의 정보를 담은 클래스를 만들고, 보석의 무게순, 가방의 무게순으로 오름차순 정렬을 한다.
- 이제 가방의 무게가 작은 순서로 하나씩 확인한다.
- 해당 가방보다 무게가 작은 보석들은 그 가방에 들어갈 수 있으므로, 들어갈 수 있는(가방보다 무게가 작은) 보석들을 Priority Queue에 넣는다.
- 해당 PQ는 보석의 가치 순으로 정렬되어 있으므로, 들어가는 보석의 가치가 높은 순으로 들어있다.
- 현재 가방에 넣을 수 있는 보석을 다 넣었으면, 현재까지 확인한 보석의 인덱스를 남겨두고, 가장 가치가 큰 보석을 가방에 넣는다(poll())
- 다음 가방을 확인하되, 현재까지 확인한 보석의 인덱스가 남아있으므로, 그 보석부터 다시 가방에 넣을 수 있는지 확인한다.
- 만약 현재 가방의 크기보다 다음 보석의 무게가 더 커서 pq에 하나에 넣지 못했더라도, 이전에 넣을 보석 중에 가장 가치가 큰 보석을 넣는다.
위 방식을 이용하면, 매 가방마다 보석들을 확인하지 않고 보석을 한번씩만 확인하므로 시간 복잡도는 O(N + K)가 된다.
이는 O(n) 이므로 충분히 시간 내에 계산 가능하다.
코드
import java.util.*;
import java.io.*;
public class Main {
static int n, k;
static long result;
static List<Jewel> jem;
static int[] bag;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stk = new StringTokenizer(br.readLine());
n = stoi(stk.nextToken());
k = stoi(stk.nextToken());
jem = new ArrayList<>();
// 무게 M, 가격 V
int m,v;
for(int i = 1; i <= n; i++) {
stk = new StringTokenizer(br.readLine());
m = stoi(stk.nextToken()); // 무게
v = stoi(stk.nextToken()); // 가격
jem.add(new Jewel(m,v));
}
// 가방 들
bag = new int[k];
for(int i = 0; i < k; i++) {
bag[i] = stoi(br.readLine());
}
// 보석 무게를 기준으로 오름차순으로 정렬
Collections.sort(jem, new Comparator<Jewel>() {
@Override
public int compare(Jewel o1, Jewel o2) {
return Integer.compare(o1.m, o2.m);
}
});
Arrays.sort(bag);
// 보석의 가격를 기준으로 내림차순으로 정렬 : root는 항상 최고의 가치를 가진 보석이다
PriorityQueue<Jewel> pq = new PriorityQueue<>(new Comparator<Jewel>() {
@Override
public int compare(Jewel o1, Jewel o2) {
return -Integer.compare(o1.v, o2.v);
}
});
int idx = 0;
// 가방을 하나씩 탐색
for(int i = 0; i < k; i++) {
// 해당 가방의 무게보다 작은 보석들을 가방에 다 넣는다.
while(true) {
if(jem.size() == idx || bag[i] < jem.get(idx).m) {
break;
}
pq.add(jem.get(idx));
idx++;
}
// 현재 담을 수 있는 보석 중에 가장 가치가 큰 보석을 가방에 넣는다.
if(!pq.isEmpty())
result += pq.poll().v;
}
System.out.println(result);
br.close();
}
static class Jewel {
int m,v;
public Jewel(int m, int v) {
super();
this.m = m;
this.v = v;
}
}
static int stoi(String str) {
return Integer.parseInt(str);
}
}
총평
후기
Knap Sack 문제인 줄 알고, 생각해보다가, 애초에 가능 할 수 없는 문제였다.
최소 O(n^2)이니, 풀 수 없는 문제였으니 미리 알아 챌 필요가 있었다.
개선할 점
없습니다.