넘치게 채우기

[BOJ] 16566 - 카드 게임 본문

PS/BOJ

[BOJ] 16566 - 카드 게임

riveroverflow 2025. 2. 6. 10:47
728x90
반응형

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

BOJ - 카드 게임

문제 유형: 분리 집합, 이진 탐색, 유니온-파인드

문제 난이도: Platinum V

시간 제한: 1.2초

메모리 제한: 512MB

 

문제

철수와 민수는 카드 게임을 즐겨한다. 이 카드 게임의 규칙은 다음과 같다.

  1. N개의 빨간색 카드가 있다. 각각의 카드는 순서대로 1부터 N까지 번호가 매겨져 있다. 이 중에서 M개의 카드를 고른다.
  2. N개의 파란색 카드가 있다. 각각의 카드는 순서대로 1부터 N까지 번호가 매겨져 있다. 이 중에서 빨간색에서 고른 번호와 같은 파란색 카드 M개를 고른다.
  3. 철수는 빨간색 카드를 가지고 민수는 파란색 카드를 가진다.
  4. 철수와 민수는 고른 카드 중에 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)
	}
}
728x90
반응형

'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