n + 1 카드게임
2024 카카오 윈터 인턴쉽 문제가 오픈되어 풀어보았다.
문제 설명
- 1 ~ n 까지의 숫자가 적혀있는 카드가 무작위로 석여있다.
- 처음에는 카드 n / 3 장을 가진다.
- 라운드가 시작될 때마다 2장을 뽑고 1장에 1개 코인을 사용해서 뽑은 카드를 사용할 수 있다.
- 라운드마다 n + 1 의 합이 되는 카드 2장을 내야한다.
- 그러지 못하면 게임은 종료된다.
최대 라운드 수를 구해라
흐름 파악
흐름 파악을 위해 꼬리에 꼬리를 무는 방식으로 접근하려한다.
최대 라운드 수를 어떻게 구하지?
카드의 수는 중복이 없으니까 각 카드별로 짝을 이룰 수 있는 수는 정해져있어
조합이 될 때마다 해당 조합을 내고 다음 라운드를 진행하면서 최대 라운드 수를 구하면 되겠네
시행착오
BFS, DFS 로 접근하려고 했는데 시간초과가 났다.
코드를 작성하고 보니 최대를 구하다보니 조기 종료 조건이 없었다.
해봤자 현재 라운드 + (남은 카드 수) / 2 < 지금까지 최대 라운드
정도인데 이 조건 또한 도움이 되지 않는다.
저 조건에 해당되기 한참 전 depth 부터 너무 많은 경우의 수가 발생하기 때문이다.
BFS, DFS 를 선택했던 이유는 모든 경우의 수를 계산하지 않고는 최대 라운드 수를 확정할 없다고 생각했기 떄문이다.
그렇지만 달리 생각해보면 뽑은 카드를 해당 라운드에서 사용하지 못해도 저장하고 있다가 필요한 숫자가 나올 때 coin 을 사용해서 사용한다면 코인을 적재적소에 사용한 것이다. 이러한 컨셉으로 라운드를 계속 진행시키면서 3가지 로직을 진행한다.
- 현재 가지고 있는 카드로 짝을 이룰 수 있는지 확인한다.
- 현재 라운드에 뽑은 카드 2장을 포함해서 이전에 가질 수 있는 카드 목록 중 1장을 사용해서 짝을 이룰 수 있는지 확인한다.
- 현재 라운드에 뽑은 카드 2장을 포함해서 이전에 가질 수 있는 카드 목록 중 2장을 사용해서 짝을 이룰 수 있는지 확인한다.
전체 코드
import random
from collections import deque
def solution(coin, cards):
answer = 1
hands = cards[0:len(cards) // 3]
remain_cards = deque(cards[len(cards) // 3:])
able_remain_cards = []
cur_round = 0
hands_dict = {v: True for v in hands}
while True:
cur_round += 1
if not hands and not remain_cards:
break
if remain_cards:
able_remain_cards.append(remain_cards.popleft())
if remain_cards:
able_remain_cards.append(remain_cards.popleft())
pair = []
for c in hands:
if (len(cards) + 1) - c in hands_dict:
pair.append(c)
pair.append((len(cards) + 1) - c)
break
if pair:
hands.remove(pair[0])
hands.remove(pair[1])
del hands_dict[pair[0]]
del hands_dict[pair[1]]
continue
if coin == 0:
break
for able_card in able_remain_cards:
if (len(cards) + 1) - able_card in hands_dict:
pair.append((len(cards) + 1) - able_card)
pair.append(able_card)
break
if pair:
hands.remove(pair[0])
able_remain_cards.remove(pair[1])
coin -= 1
del hands_dict[pair[0]]
continue
if coin == 1:
break
able_remain_card_dict = {v: True for v in able_remain_cards}
for c in able_remain_cards:
if (len(cards) + 1) - c in able_remain_card_dict:
pair.append(c)
pair.append((len(cards) + 1) - c)
break
if pair:
able_remain_cards.remove(pair[0])
able_remain_cards.remove(pair[1])
coin -= 2
continue
break
return cur_round
print(solution(1000, random.sample([i for i in range(1000)], 1000)), 500)
print(solution(4, [3, 6, 7, 2, 1, 10, 5, 9, 8, 12, 11, 4]), 5)
print(solution(3, [1, 2, 3, 4, 5, 8, 6, 7, 9, 10, 11, 12]), 2)
print(solution(2, [5, 8, 1, 2, 9, 4, 12, 11, 3, 10, 6, 7]), 4)
print(solution(10, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]), 1)