[ 백준 ] 9658 / 돌 게임 4

金弘均·2021년 9월 15일
0

Baekjoon Online Judge

목록 보기
133/228
post-thumbnail
post-custom-banner

# Appreciation

/*
 * Problem :: 9658 / 돌 게임 4
 *
 * Kind :: Dynamic Programming
 *
 * Insight
 * - 답을 공식을 구해서 해결하려 했는데... 어렵다
 *   좀 무식하게 풀 수 없을까?
 *   + 돌이 1개 남으면 상근이가 무조건 진다
 *     한 번에 1,3,4 개를 가져갈 수 있으니까
 *     그럼 돌이 2,4,5개 남으면 상근이가 무조건 이기네?
 *     # 무조건 지는 돌의 개수가 N 개면
 *       돌이 N+1, N+3, N+4 개 남으면 상근이가 무조건 이긴다
 *       -> 그럼 N+2 개 남으면 무조건 진다는 건가...?
 *
 * - 일단 dp[N] 을 다 진다고 해놓고
 *   dp[i] == LOSE 일 때
 *   dp[i+1], dp[i+2], dp[i+4] 는 WIN 으로 바꾸어 주었다
 *   + 이렇게 풀었더니 답은 맞았다
 *     규칙이 없을까 해서 한번 N=100 으로 하고 남은 돌의 개수에 따라 누가 이기는지 알아봤다
 *     # 남은 돌의 개수 : 누가 이기는가
 *                 1 : 창영
 *                 2 : 상근
 *                 3 : 창영
 *                 4 : 상근
 *                 5 : 상근
 *                 6 : 상근
 *                 7 : 상근
 *
 *                 8 : 창영
 *                 9 : 상근
 *                10 : 창영
 *                11 : 상근
 *                12 : 상근
 *                13 : 상근
 *                14 : 상근
 *
 *                15 : 창영
 *                16 : 상근
 *                17 : 창영
 *                18 : 상근
 *                19 : 상근
 *                20 : 상근
 *                21 : 상근
 *
 *                ~  :  ~
 *       -> 7씩 반복된다...?!?
 *
 * - 돌을 한 번에 1,2,3 개 가져가는 경우는
 *   4씩 반복된다 (창영, 상근, 상근, 상근, 창영, 상근, 상근 상근, ...)
 *   확실히 무슨 규칙이 있는것 같다...!!!
 *   + 돌을 한 번에 1,2,3 개 가져갈 수 있고 나머지 돌을 가져가면 지는 경우
 *     일단 남은 돌이 N개 이하인 모든 상태를 '확실히 지는 경우' 로 초기화 한다
 *     L = lose, W = win
 *     # dp[1] : L
 *       dp[2] : W (1 + 1)
 *       dp[3] : W (1 + 2)
 *       dp[4] : W (1 + 3) => 확실히 지는 경우(돌이 1개 남은 경우)를 바탕으로
 *                            확실히 이기는 경우(돌이 2,3,4 개 남은 경우)를 알 수 있다
 *       dp[5] : L => 게임에 참가하는 사람들이 모두 완벽하게 게임을 하므로,
 *                    가능한 상태는 '확실하게 이기는 경우' 와 '확실하게 지는 경우' 둘 뿐이다
 *                    남은 돌이 5개 미만인 이전의 경우에 대해서 모두 살펴보았는데도
 *                    현재 상태가 '확실하게 지는 경우' 라면
 *                    이 남은 돌을 바탕으로 이기는 시나리오가 없다는 뜻이다
 *       dp[6] : W => (5 + 1)
 *       dp[7] : W => (5 + 2)
 *       dp[8] : W => (5 + 3)
 *       -> 확실히 지는 경우인 남은 돌의 개수 1을 바탕으로 얻을 수 있는 정보는
 *          남은 돌이 2,3,4 개일 때 확실히 이긴다는 것이다
 *          그 다음 남은 돌의 개수 5개는
 *          그 이전의 경우를 살펴보아도 이기는 시나리오가 존재하지 않았으므로
 *          확실히 지는 경우임을 알 수 있다
 *          그러면 이 정보를 바탕으로 6,7,8 개일 때 확실히 이긴다는 것을 알 수 있다
 *          그 다음 남은 돌의 개수 9개는
 *          그 이전의 경우를 살펴보아도 이기는 시나리오가 존재하지 않았으므로
 *          확실히 지는 경우임을 알 수 있다
 *          그러면 이 정보를 바탕으로 9,10,11 개일 때 확실히 이긴다는 것을 알 수 있다
 *          ... 반복
 *   + 돌을 한 번에 1,3,4 개 가져가는 경우는
 *     7씩 반복된다 (창영, 상근, 창영, 상근, 상근, 상근 상근, ...)
 *     일단 남은 돌이 N개 이하인 모든 상태를 '확실히 지는 경우' 로 초기화 한다
 *     L = lose, W = win
 *     # dp[1] : L
 *       dp[2] : W => (1 + 1)
 *       dp[3] : L
 *       dp[4] : W => (1 + 3), (3 + 1)
 *       dp[5] : W => (1 + 4)
 *       dp[6] : W => (3 + 3)
 *       dp[7] : W => (3 + 4)
 *       dp[8] : L
 *       -> 확실히 지는 경우인 남은 돌의 개수 1을 바탕으로 얻을 수 있는 정보는
 *          남은 돌이 2,4,5 개일 때 확실히 이긴다는 것이다
 *          중요한 점은, 여기서 남은 돌이 3개 일 때 확실히 진다는 정보도 얻을 수 있다는 것이다
 *          이를 토대로 4,6,7 개일 때 확실히 이긴다는 정보 또한 알 수 있다
 *          그 다음 남은 돌의 개수 8개는
 *          그 이전의 경우를 살펴보아도 이기는 시나리오가 존재하지 않았으므로
 *          확실히 지는 경우임을 알 수 있다
 *          그러면 이 정보를 바탕으로 9,11,12 개일 때 확실히 이긴다는 것을 알 수 있다
 *          중요한 점은, 여기서 남은 돌이 10개 일 때 확실히 진다는 정보도 얻을 수 있다는 것이다
 *          이를 토대로 11,13,14 개일 때 확실히 이긴다는 정보 또한 알 수 있다
 *          그 다음 남은 돌의 개수 15개는
 *          그 이전의 경우를 살펴보아도 이기는 시나리오가 존재하지 않았으므로
 *          확실히 지는 경우임을 알 수 있다
 *          그러면 이 정보를 바탕으로 16,18,19 개일 때 확실히 이긴다는 것을 알 수 있다
 *          중요한 점은, 여기서 남은 돌이 17개 일 때 확실히 진다는 정보도 얻을 수 있다는 것이다
 *          이를 토대로 18,20,21 개일 때 확실히 이긴다는 정보 또한 알 수 있다
 *          ... 반복
 *
 * Point
 * - DP 였지만 알고보니 DP 를 표방한 Math 문제?
 */

# Code

//
//  BOJ
//  ver.C++
//
//  Created by GGlifer
//
//  Open Source

#include <iostream>
#include <cstring>

using namespace std;

#define endl '\n'

// Set up : Global Variables
#define WIN true
#define LOSE false

// Set up : Functions Declaration
/* None */


int main()
{
    // Set up : I/O
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // Set up : Input
    int N; cin >> N;

    // Process
    bool dp[N+1];
    /* 남은 돌이 N개 이하인 모든 상태를 '확실히 지는 경우' 로 초기화 */
    /* 논리상, dp[0] = WIN 으로 초기화 하는 것이 맞지만
     * 결과에 아무 영향을 끼치지 않으므로 무시했음 */
    memset(dp, LOSE, sizeof(dp));
    dp[1] = LOSE; /* 돌이 1개 남은 경우에는 확실히 짐 */
    for (int i=1; i<=N; i++) {
        /* 이전의 경우에서 확실히 이길 수 있는 가능한 경우를 모두 찾았음에도 불구하고
         * 여전히 LOSE 로 남아있다면, 지금 다루고 있는 남은 돌 i 개의 경우는 확실히 지는 경우임 */
        if (dp[i] == LOSE) { /* 확실히 지는 경우를 토대로 */
            if (i+1 <= N) { dp[i+1] = WIN; } /* 확실히 이기는 경우를 찾아줌 */
            if (i+2 <= N) { dp[i+2] = WIN; }
            if (i+3 <= N) { dp[i+3] = WIN; }
        }
    }

    // Control : Output
    cout << ((dp[N] == WIN) ? "SK" : "CY") << endl;

    /* 규칙을 찾으면 아래처럼 풀 수도 있음 */
    // switch (--N % 7) {
    //     case 0:
    //     case 2:
    //         cout << "CY" << endl;
    //         break;
    //     case 1:
    //     case 3:
    //     case 4:
    //     case 5:
    //     case 6:
    //         cout << "SK" << endl;
    //         break;
    // }
}

// Helper Functions
profile
이런 미친 게임을 봤나! - 옥냥이
post-custom-banner

0개의 댓글