Algorithm/백준

boj21939: 문제 추천 시스템 Version 1

미네구스 2025. 5. 15. 23:48

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

 

접근 방법

최근 본 코테와 비슷한 문제였다. 우선순위 큐에 [문제번호, 난이도] 형태로 값을 집어넣고 문제번호나 난이도 조건에 의해 정렬이 되어있다고 가정하자. 만약에, 이 때 무작위의 문제번호를 제거하라고 한다면 어떻게 할 것인가?

코테에서도 그렇고, 이 문제를 처음 접근할 때 이 아이디어를 내지 못해서 틀렸다. 다른 사람의 아이디어를 참고하니 우선순위 큐 이외의 `해쉬맵`을 선언하여 접근한 것을 볼 수 있었다.

 

문제에서 recommend가 1과 -1일 때 각각 조건이 다른데, 어려운 문제를 우선적으로 뽑는 큐와 쉬운 문제를 우선적으로 뽑는 큐가 필요했다. 나는 max_pq, min_pq로 선언해서 각각의 조건에 맞게 세팅을 해주었다. 

 

해쉬맵을 만든 이유는 간단하다. 문제에서 `solved`를 수행할 때, 문제번호 P를 제거를 해야하는데 우선순위 큐의 특성 상 P를 바로 지울 수 없다. 그렇기 때문에 해쉬맵에서 해당 P를 삭제를 먼저 해주는 것이다. 

 

solved가 계속해서 수행되고나서 recommend가 수행되면 해쉬맵과 큐의 불일치가 발생할 것이다.

해쉬맵에는 문제번호가 없는데 큐에는 있다!! 이 말의 뜻은 큐에 있는 문제번호와 해쉬맵의 문제번호가 일치할 때까지 큐의 값을 삭제해줘야하고, 일치할 때 비로소 문제를 출력해주면 된다.

 

직관적인 예시

max_pq = [1000, 1], [1001,2], [19998, 78], [2667,37], [2042,55]가 있을 때,

map 또한 [1000, 1], [1001,2], [19998, 78], [2667,37], [2042,55] 이렇게 가지고 있을 것이다.

 

solved 19998가 실행되면,

map = [1000, 1], [1001,2], [2667,37], [2042,55] 으로 문제번호 19998이 삭제되지만,

max_pq = [1000, 1], [1001,2], [19998, 78], [2667,37], [2042,55]는 그대로이다.

 

이 때, recommend 1을 실행했을 때, max_pq에 있는 문제번호 19998이 map에는 없다. 따라서, max_pq의 현재 문제번호와 난이도가 map과 일치할 때 까지 큐를 삭제하면 된다. (위 케이스의 경우 하나만 삭제해주면 된다.)

 

 

이러한 과정을 max_pq, min_pq 동시에 해주면 된다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class boj21939_2 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        // 문제번호, 난이도 형태
        PriorityQueue<int[]> max_pq = new PriorityQueue<>((a,b) -> {
            if (a[1] == b[1]) return b[0] - a[0];
            return b[1] - a[1];
        });

        PriorityQueue<int[]> min_pq = new PriorityQueue<>((a, b) -> {
            if (a[1] == b[1]) {
                return a[0] - b[0];
            }
            return a[1] - b[1];
        });

        Map<Integer, Integer> map = new HashMap<>(); // 제거할 때 쓰는 용도

        for(int i = 0; i < n; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            max_pq.add(new int[]{a,b});
            min_pq.add(new int[]{a,b});
            map.put(a,b);
        }

        int m = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < m; i++) {
            String input = br.readLine();
            String [] arr = input.split(" ");

            if (arr[0].equals("add")) {
                int a = Integer.parseInt(arr[1]);
                int b = Integer.parseInt(arr[2]);
                max_pq.add(new int[]{a,b});
                min_pq.add(new int[]{a,b});
                map.put(a,b); // 모두 다 추가해줌
            }

            else if (arr[0].equals("solved")) {
                int a = Integer.parseInt(arr[1]);
                map.remove(a); // 맵에서만 제거해준다.
            }

            else if (arr[0].equals("recommend")) {
                int a = Integer.parseInt(arr[1]);
                if (a == 1) { // 추천리스트에 가장 어려운 번호를 출력
                    while (true) {
                        int num = max_pq.peek()[0];
                        int diff = max_pq.peek()[1];
                        if (map.containsKey(num) && map.get(num) == diff) {
                            sb.append(max_pq.peek()[0]).append("\n");
                            break;
                        }

                        max_pq.poll();
                    }
                }

                else {
                    while (true) {
                        int num = min_pq.peek()[0];
                        int diff = min_pq.peek()[1];
                        if (map.containsKey(num) && map.get(num) == diff) {
                            sb.append(min_pq.peek()[0]).append("\n");
                            break;
                        }

                        min_pq.poll();
                    }
                }
            }
        }
        System.out.println(sb);
    }
}