-
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(); } }
반응형'코딩테스트 > 백준_Java' 카테고리의 다른 글
Q. 1981 자바 : k번째 최단경로 찾기 (0) 2025.06.01 Q. 1981 자바 : 배열에서 이동 (0) 2025.05.25 Q. 3197 자바 : 백조의 호수 (0) 2025.05.11 Q. 1637 자바 : 날카로운 눈 (0) 2025.03.02 Q. 5719 자바 : 거의 최단 경로 (0) 2025.01.26