알고리즘

프로그래머스 - n + 1 카드 게임

SniKuz 2024. 3. 13. 21:07

링크 : https://school.programmers.co.kr/learn/courses/30/lessons/258707

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

문제 설명

1, 2, ..., n이 적혀있는 카드가 하나씩 있는 덱과 동전을 이용해서 게임을 하려고 합니다.
카드뭉치는 위에서부터 차례대로 뽑으며, 게임의 룰은 다음과 같습니다.\

1. 처음에 coin개의 코인과 n/3 만큼의 카드를 손패로 가집니다. (n은 6의 배수입니다)

2. 게임은 1라운드부터 시작되며, 각 라운드가 시작될 때 덱 맨 위 카드 2장을 공개합니다. 만약 더 이상 뽑을 카드가 없다면 게임을 종료합니다.
공개된 카드는 각각 동전 1개를 소모해 가져갈 수 있습니다.

3. 카드에 적힌 수의 합이 n+1이 되도록 카드를 2장 내고 다음 라운드로 넘어갑니다. 만약 카드 두 장을 낼 수 없다면 게임을 종료합니다.


아이디어

● 종료조건 
게임의 종료조건은 "1. 뽑을 카드가 더이상 없다" "2. 더이상 카드를 낼 수 없다" 2가지 입니다.
"1. 뽑을 카드가 더이상 없다"는 덱에서 손패를 뽑은 후 남은 카드 수와 매 라운드 2장씩 카드를 뽑는 것을 통해 최대 n/3번 뽑을 수 있다는 것을 알 수 있습니다.

"2. 더 이상 카드를 낼 수 없다"는 들고있던 손패와 코인을 사용해 다음 카드 조합을 얻을 수 없을 때 종료됩니다.
여기서 매 라운드 얻을 수 있는 카드를 미리 결정하지 않고 나중에 선택해도 문제가 되지 않습니다.

● 코인 사용

1~n의 연속된 수로 n+1을 만드는 방법은 (1+n), (2+(n-1)) ... 와 같이 고유세트로만 만들 수 있습니다. 

기존에 손에 들고있던 카드들로만 n+1을 만들 수 있다면 0코인,
손에 들고 있는 카드와 덱에서 공개된 카드로 만들 수 있다면 1코인,
덱에서 공개된 카드 2장으로 만들 수 있다면 2코인을 소비한다고 생각할 수 있습니다.

그러면 손패와 덱에서 공개한 카드를 따로 기록하며, 만약 n+1을 만들 수 있는 신규 조합이 발견되면 몇 코인을 써서 만들 수 있는지 기록하고, 우선순위 큐에 넣어 매 라운드마다 1개씩 꺼내주는 방식이 가능할 것 같습니다.


코드

#include <vector>
#include <set>
#include <queue>
#include <algorithm>

using namespace std;

int n, targetNum, turn = 0, i, _coin;
//손패
set<int> ad; 
//덱에서 이미 공개한 카드들
set<int> grave; 
//낼 수 있는 카드조합. 0, 1, 2 : 각각 0 ~ 2코인 소모 else : 의미X
priority_queue<int> readyToUseCards; 
vector<int> cardNotUsed(1001, 1); 


bool UseCard()
{
    if(readyToUseCards.empty()) return false;
    auto useCard = readyToUseCards.top(); readyToUseCards.pop();
    
    if(_coin < -useCard)
        return false;

    _coin += useCard;
    return true;
}

int solution(int coin, vector<int> cards) {
    int n = cards.size();
    int targetNum = n + 1;
    _coin = coin;

    for(i = 0; i < n / 3; ++i)
    {
        ad.insert(cards[i]);
        if(cardNotUsed[cards[i]] && ad.find(targetNum - cards[i]) != ad.end())
        {
            cardNotUsed[cards[i]] = 0;
            cardNotUsed[targetNum - cards[i]] = 0;
            readyToUseCards.push(0);
        }
    }
    
    
    for(; i < n; i = i + 2)
    {
        turn++;
        int first = cards[i];
        int second = cards[i+1];
        if(cardNotUsed[first] && ad.find(targetNum - first) != ad.end())
        {
            readyToUseCards.push(-1);
            cardNotUsed[first] = 0;
            cardNotUsed[targetNum - first] = 0;
        }
        else
        {
            grave.insert(first);
            if(cardNotUsed[first] && grave.find(targetNum - first) != grave.end())
            {
                readyToUseCards.push(-2);
                cardNotUsed[first] = 0;
                cardNotUsed[targetNum - first] = 0;
            }
        }
        if(cardNotUsed[second] && ad.find(targetNum - second) != ad.end())
        {
            readyToUseCards.push(-1);
            cardNotUsed[second] = 0;
            cardNotUsed[targetNum - second] = 0;
        }
        else
        {
            grave.insert(second);
            if(cardNotUsed[second] && grave.find(targetNum - second) != grave.end())
            {
                readyToUseCards.push(-2);
                cardNotUsed[second] = 0;
                cardNotUsed[targetNum - second] = 0;
            }
        }

        if(!UseCard())
            break;
    }
    
    if(i == n) turn++;
    return turn;
}

성능요약 : 4.2MB, 0.06ms


꼭 오타를 애초에 안만들게 머리속에 생각이 끝나면 쓰기, 생각 정리 하기