https://www.acmicpc.net/problem/1202
1202번: 보석 도둑
첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000) 다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000) 다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ci
www.acmicpc.net
이 문제의 풀이 계획
- 보석을 가치는 높고 가치가 같다면 무게가 낮은 순으로 정렬을 한다.
- 보석을 순회 하면서 넣을수 있는 가방 중에 가장 무게가 낮은 가방을 고르면 된다.
2번이 일반적인 풀이는 우선순위큐를 사용해서 쓸 수 있는 가방을 계속 넣었다 빼줬다 하면서 풀 수 있다.
여기서 나는 이진탐색 + union-find 의 find 알고리즘으로 풀이를 해보았다.
import java.io.*;
import java.util.*;
public class Main {
static class Gem implements Comparable<Gem> {
int m;
int price;
public Gem(int m, int price) {
this.m = m;
this.price = price;
}
@Override
public int compareTo(Gem o) {
return o.price == this.price ? this.m - o.m : o.price - this.price;
}
}
static int N, K;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
PriorityQueue<Gem> priorityQueue = new PriorityQueue<>();
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
priorityQueue.add(new Gem(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken())));
}
int[] bags = new int[K];
for (int i = 0; i < K; i++) {
bags[i] = Integer.parseInt(br.readLine());
}
System.out.println(getMaxValue(priorityQueue, bags));
}
private static long getMaxValue(PriorityQueue<Gem> priorityQueue, int[] bags) {
Arrays.sort(bags);
int[] parents = new int[K + 1];
for (int i = 1; i <= K; i++) {
parents[i] = i;
}
long values = 0;
while (!priorityQueue.isEmpty()) {
Gem cur = priorityQueue.poll();
if (findBag(parents, bags, cur.m)) {
values += cur.price;
}
}
return values;
}
private static boolean findBag(int[] parents, int[] bags, int m) {
int idx = binarySearch(bags, m);
if (idx < K) {
int parent = find(parents, idx);
if (parent < K) {
parents[parent] = parent + 1;
return true;
}
}
return false;
}
private static int find(int[] parents, int node) {
if (parents[node] == node) {
return node;
}
return parents[node] = find(parents, parents[node]);
}
private static int binarySearch(int[] bags, int m) {
int left = 0;
int right = bags.length - 1;
while (left <= right) {
int mid = (left + right) >> 1;
if (bags[mid] < m) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
}
아 물론 처음에 정렬은 우선순위큐를 사용하였지만 여기서는 그냥 정렬로 써도 무방하다. (근데 속도는 우선순위 큐가 가장 빠르더라..)
함수 소개
func : getMaxValue() => 가장 가치가 높으면서 무게가 낮은 보석을 순차적으로 순회하면서
func : binarySearch() => 넣을 수 있는 가방이 있는지 이진탐색으로 탐색한다.
있는 경우,
이 때 이미 사용한 가방은 사용할 수가 없는데, 배열에서 중간 원소를 없애기는 비용이 너무 크므로 방문 체크만 해주고 그 다음 사용 가능한 원소를 저장해두는 방식을 떠올렸다.
func : find() => union-find 알고리즘에서 find 알고리즘을 따온 것이고 사용한 가방의 다음 번호를 저장한다. 그럼 연쇄적으로는 다음 가방도 사용한 가방이면 끝까지 가서 사용 안 된 다음 가방을 찾을 것이다.
경로 최적화 부분으로 이 알고리즘은 상수의 시간 복잡도를 갖는다고 한다.
parents[node] = find(parents, parents[node]);
후기:
우선순위큐를 사용하는 풀이보다 이게 더 시간 빠르게 나온 것 같아서 이런 풀이도 있다~ 입니다.
'algorithm' 카테고리의 다른 글
백준 - 1987 ( dfs, backtracking, bitmask ) (1) | 2022.12.08 |
---|
댓글