ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Q. 16566 자바 : 카드 게임
    코딩테스트/백준_Java 2025. 5. 18. 22:55

    Q. 16566 자바 : 카드 게임

     

    문제 : boj.kr/16566

    P5 난이도의 문제이다.

     

    문제를 간단히 설명하자면

    카드 더미가 주어진다. 이 때 상대는 나와 동일한 카드 더미를 가진다.

    그리고 상대의 카드가 주어질 때 상대의 카드보다 크면서 이전에 낸 적 없는 가장 작은 카드를 출력하는 문제이다.


    먼저 완전 탐색 방식으로 푼다고 가정하면 아래와 같이 풀었을 것 같다.

    1. 상대의 카드보다 크면서 이전에 낸 적 없는 카드 중 가장 작은 카드를 카드 더미에서 찾는다.

    2. 사용한 카드는 사용했다고 처리한다.

    3. 1~2 과정을 반복한다.

     

    이렇게 푼다면 낼 수 있는 카드를 찾을 때 N^2번의 연산이 생기고,

    이전에 낸 적 없는 카드를 찾는데 최악의 경우 N번의 연산이 생기므로 총 N^3 * K의 시간복잡도를 가지게 된다.

    시간제한은 1.2초이므로 이 방법은 불가능해 보였다.

     

    K는 어쩔 수 없지만, N의 경우 N log N 정도로 시간복잡도를 줄여야 한다.


    이 문제에서 중요한 점은 두 가지이다.

    1. 상대방의 카드보다 크면서 가장 작은 카드를 카드 더미에서 찾는다. - 이분 탐색 O(N log N)

    2. 이전에 사용했던 카드는 사용할 수 없다. - untion-find O(1)

     

    1번 과정은 이분 탐색을 사용한다면 N log N의 시간 복잡도로 탐색 시간을 줄일 수 있다.

     

    2번 과정도 N번의 연산이 필요하고, 이를 줄이기 위한 아이디어를 떠올리는데 어려웠다.

    핵심 아이디어는 n번 카드가 이미 사용됬다면, 그 다음에 사용할 수 있는 카드는 n+1번 카드라는 점 이다.

    union-find를 사용한다면 이 연산을 상수 시간으로 줄일 수 있다.


    처음에는 모든 카드가 사용되지 않았으므로 부모를 나 자신으로 갖는다. - parent[i] = i

    1번 과정으로 카드를 사용했다면 사용한 카드의 부모는 내 다음 카드로 대입한다. - union(n, n+1)

    이 때 n + 1이 전체 카드 숫자의 범위를 초과할 수 있으므로 예외 처리가 필요하다.

     

    2번의 연산 속도는 상수이므로 전체 시간 복잡도를 O(K*N logN)으로 줄일 수 있다.

     

    코드는 아래와 같다.

    import java.io.*;
    import java.util.*;
    
    class Main {
    
        static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        static StringBuffer sb = new StringBuffer();
        static StringTokenizer st;
    
        static int n, m, k, curSubmit;
        static int[] cards, submits, parents;
    
        static void input() throws IOException{
            st = new StringTokenizer(br.readLine());
    
            n = Integer.parseInt(st.nextToken());
            m = Integer.parseInt(st.nextToken());
            k = Integer.parseInt(st.nextToken());
    
            cards = new int[m];
            st = new StringTokenizer(br.readLine());
            for (int i = 0; i < m; i++) {
                cards[i] = Integer.parseInt(st.nextToken());
            }
    
            submits = new int[k];
            st = new StringTokenizer(br.readLine());
            for (int i = 0; i < k; i++) {
                submits[i] = Integer.parseInt(st.nextToken());
            }
        }
    
        static void pro() {
    
            Arrays.sort(cards);
            parents = new int[m];
            for (int i = 0; i < m; i++) parents[i] = i;
    
            for (int i = 0; i < k; i++) {
                curSubmit = submits[i];
                int result = 0;
    
                int left = 0, right = m - 1;
                while (left <= right) {
                    int mid = (left + right) / 2;
                    int available = find(mid);
    
                    if (determination(cards[available])) {
                        result = available;
                        right = mid - 1;
                    } else {
                        left = mid + 1;
                    }
                }
    
                sb.append(cards[result]).append('\n');
                if (result + 1 != m) union(result + 1, result);
            }
            System.out.println(sb);
        }
    
        static int find(int p) {
            if (parents[p] == p) return p;
            return parents[p] = find(parents[p]);
        }
        
        static void union(int a, int b) {
            int aParent = find(a);
            int bParent = find(b);
    
            parents[bParent] = aParent;
        }
    
        static boolean determination(int element) {
            return curSubmit < element;
        }
    
        public static void main(String[] args) throws Exception{
            input();
            pro();
        }
    }

     

    반응형

    댓글

Designed by Tistory.