넘치게 채우기
[BOJ] 16566 - 카드 게임 본문
https://www.acmicpc.net/problem/16566
BOJ - 카드 게임
문제 유형: 분리 집합, 이진 탐색, 유니온-파인드
문제 난이도: Platinum V
시간 제한: 1.2초
메모리 제한: 512MB
문제
철수와 민수는 카드 게임을 즐겨한다. 이 카드 게임의 규칙은 다음과 같다.
- N개의 빨간색 카드가 있다. 각각의 카드는 순서대로 1부터 N까지 번호가 매겨져 있다. 이 중에서 M개의 카드를 고른다.
- N개의 파란색 카드가 있다. 각각의 카드는 순서대로 1부터 N까지 번호가 매겨져 있다. 이 중에서 빨간색에서 고른 번호와 같은 파란색 카드 M개를 고른다.
- 철수는 빨간색 카드를 가지고 민수는 파란색 카드를 가진다.
- 철수와 민수는 고른 카드 중에 1장을 뒤집어진 상태로 낸다. 그리고 카드를 다시 뒤집어서 번호가 큰 사람이 이긴다. 이 동작을 K번 해서 더 많이 이긴 사람이 최종적으로 승리한다. 한 번 낸 카드는 반드시 버려야 한다.
철수는 뛰어난 마술사이기 때문에 본인이 낼 카드를 마음대로 조작할 수 있다. 즉, 카드를 버리고 민수 몰래 다시 들고 온다거나 민수한테 없는 카드를 내기도 한다.
민수는 뛰어난 심리학자이기 때문에 철수가 낼 카드를 알아낼 수 있다. 그래서 민수는 철수가 낼 카드보다 큰 카드가 있다면 그 카드들 중 가장 작은 카드를 내기로 했다.
K번 동안 철수가 낼 카드가 입력으로 주어진다. 그렇다면 민수가 어떤 카드를 낼지 출력하라. 단, 민수가 카드를 내지 못하는 경우는 없다고 가정한다.
입력
첫째 줄에 세 개의 자연수 N, M, K가 주어진다. (1 ≤ M ≤ N ≤ 4,000,000, 1 ≤ K ≤ min(M, 10,000))
다음 줄에 카드의 번호를 나타내는 M개의 자연수가 주어진다. 각각의 수들은 1 이상이고 N 이하이며 서로 다르다.
다음 줄에 K개의 자연수가 주어진다. i번째 수는 철수가 i번째로 내는 카드의 번호이다. 철수가 내는 카드 역시 1 이상 N 이하이다.
출력
K줄에 걸쳐서 수를 출력한다. i번째 줄에는 민수가 i번째로 낼 카드의 번호가 출력되어야 한다.
풀이
두 가지 풀이가 있다. 공통점은 유니온-파인드라는 것.
1. 무식하게 1부터 n까지의 parent배열을 만들기
(10775 - 공항)과 97% 같다.
10775 - 공항에 대한 풀이: https://riveroverflow.tistory.com/entry/BOJ-10775-%EA%B3%B5%ED%95%AD
[BOJ] 10775 - 공항
https://www.acmicpc.net/problem/10775BOJ - 공항문제 유형: 분리 집합, 그리디문제 난이도: Gold II시간 제한: 1초메모리 제한: 256MB 문제오늘은 신승원의 생일이다.박승원은 생일을 맞아 신승원에게 인천국
riveroverflow.tistory.com
대신, 이미 존재하지 않는 곳에 대해서는 i+1을 parent로 하고, 카드 자체는 자기 자신을 parent로 하면 된다.
거기에, 철수가 내는 카드에서 +1을 한 것으로 반영하면 된다.
이 풀이는 C++말고는 MLE가 날 가능성이 크다..
2. 정렬된 카드배열과 m개의 카드들에 대한 parent배열 이용하기
cards배열에 m개의 카드 정보를 담아서 저장하고 오름차순 정렬한다.
parent배열도 m+1사이즈만 만들어준다. 대신, 인덱스기반이다.
(철수가 낸 카드숫자 +1)의 값을 cards배열에서 이진탐색해서 인덱스를 구한다.
cards[구한 인덱스]가 답이고,
parent[구한 인덱스] = (다음으로 큰 숫자 카드..의 부모)로 해준다.
코드
C++(풀이 1)
#include <bits/stdc++.h>
using namespace std;
int getRoot(int x, vector<int> &parent) {
if (parent[x] == x)
return x;
return parent[x] = getRoot(parent[x], parent);
}
int main(int argc, char *argv[]) {
ios_base::sync_with_stdio(0);
cin.tie(0);
int n, m, k;
cin >> n >> m >> k;
vector<int> parent(n + 2);
iota(parent.begin(), parent.end(), 1);
for (int i = 0; i < m; ++i) {
int card;
cin >> card;
parent[card] = card;
}
for (int i = 0; i < k; ++i) {
int opponentCard;
cin >> opponentCard;
opponentCard++;
int ans = getRoot(opponentCard, parent);
parent[ans] = parent[ans + 1];
cout << ans << "\n";
}
return 0;
}
Go(풀이 2)
package main
import (
"bufio"
"fmt"
"os"
"sort"
)
func getRoot(x int32, parent []int32) int32 {
if parent[x] == x {
return x
}
parent[x] = getRoot(parent[x], parent)
return parent[x]
}
func lowerBound(cards []int32, target int32) int32 {
l, r := int32(0), int32(len(cards))
for l < r {
mid := l + (r-l)/2
if cards[mid] < target {
l = mid + 1
} else {
r = mid
}
}
return l
}
func main() {
reader := bufio.NewReader(os.Stdin)
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
var n, m, k int32
fmt.Fscan(reader, &n, &m, &k)
cards := make([]int32, m)
for i := int32(0); i < m; i++ {
fmt.Fscan(reader, &cards[i])
}
sort.Slice(cards, func(i, j int) bool { return cards[i] < cards[j] })
parent := make([]int32, m+1)
for i := int32(0); i <= m; i++ {
parent[i] = i
}
for i := int32(0); i < k; i++ {
var opponentCard int32
fmt.Fscan(reader, &opponentCard)
targetValue := opponentCard + 1
candidate := lowerBound(cards, targetValue)
candidate = getRoot(candidate, parent)
fmt.Fprintln(writer, cards[candidate])
parent[candidate] = getRoot(candidate+1, parent)
}
}
'PS > BOJ' 카테고리의 다른 글
[BOJ] 17143 - 낚시왕 (0) | 2025.02.05 |
---|---|
[BOJ] 2162 - 선분 그룹 (0) | 2025.02.04 |
[BOJ] 15649 - N과 M (1) (0) | 2025.02.03 |
[BOJ] 2583 - 영역 구하기 (0) | 2025.02.03 |
[BOJ] 7562 - 나이트의 이동 (0) | 2025.02.03 |