백준 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)이니, 풀 수 없는 문제였으니 미리 알아 챌 필요가 있었다.
개선할 점
없습니다.
