[ 백준 ] 4811 / 알약

金弘均·2021년 9월 16일
1

Baekjoon Online Judge

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

# Appreciation

/*
 * Problem :: 4811 / 알약
 *
 * Kind :: Dynamic Programming + Math
 *
 * Insight
 * - 일단은 알약 반개를 꺼내려면 그전에 알약 한개를 꺼내야 한다
 *   + W W W H H H 는 알약 한개를 연달아 세번 꺼냈으니
 *     알약 반개를 연달아 세번 꺼낼 수 있지만
 *     W H H W W H 는 알약 한개를 꺼냈는데 그 다음에 알약 반개를 연달아 두번 꺼내니
 *     불가능한 문자열이다
 *     # 즉 문자열에서 H 가 있을 때, 그 때까지 나온 H 의 개수는 W 의 개수보다 많을 수 없다
 *
 * - N=1 / W H / 1가지
 *   + (W,H) 1쌍
 *   N=2 / W W H H, W H W H / 2가지
 *   + (W,H) 2쌍
 *   N=3 / W W W H H H, W W H H W H, W H W H W H, W H W W H H, W W H W H H / 5가지
 *   + (W,H) 3쌍
 *   ...
 *   + N=k 일 때, 문자열을
 *     W (A) H (B) 로 나타낼 수 있다.
 *     A 문자열에 (W,H) i 쌍이 들어간다면
 *     B 문자열에 (W,H) k-1-i 쌍이 들어가야 한다
 *     # 중요한 것은 A 문자열, B 문자열 모두 W 로 시작하고 H 로 끝나기 때문에
 *       위의 정의에서 겹치는 경우는 발생하지 않는다!
 *       -> N=2 일 때,
 *          W () H (W H) => W H W H
 *          W (W H) H () => W W H H
 *
 * - 따라서 점화식은 다음과 같다
 *   dp[i] = dp[0] * dp[i-1]
 *           + dp[1] * dp[i-2]
 *           + ...
 *           + dp[i-2] * dp[1]
 *           + dp[i-1] * dp[0]
 *
 * Point
 * - 예제가 꽤 친절하다
 *   + dp[1], dp[2], dp[3], dp[4] 값도 알려주고
 *     dp[30] 의 값도 알려줘서 Overflow 주의를 준다
 *
 * - 사실 이 문제는 '카탈란 수'를 다루는 문제이다
 *   + W 를 '(' 로 H 를 ')' 로 본다면
 *     이는 N 쌍의 괄호가 주어졌을 때 올바른 괄호열이 모두 몇개인지를 구하는 문제와 같다
 *     # 신기신기 ~ (카탈란 수를 검색해보자)
 *
 * - 솔직히 고백하건대 처음 풀때는 그냥 감으로 풀었다
 *   알약 3개는 알약 1개와 알약 2개의 경우로 나눌 수 있지 않을까? 하다가
 *   우연히 알약 3개의 경우의 수가
 *   (알약 0개 * 알약 2개 + 알약 1개 * 알약 1개 + 알약 2개 * 알약 0개) 와 같아서
 *   이렇게 풀면 되지 않을까 하고 찍었는데 맞았다
 *   # 다른 사람들의 풀이를 봤는데
 *     dp[i][j] = dp[i-1][j] + dp[i][j-1] 로 많이 풀었다
 *     -> 사실 바로 위의 풀이가 좀더 직관적이고 생각하기 쉬운 것 같다
 *        내심 나는 쉽게 생각할 수 있는 것을 어렵게 생각하는 습관이 있는 것 같아 좀 걱정된다...
 */

# Code

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

#include <iostream>
#include <cstring>

using namespace std;

#define endl '\n'

// Set up : Global Variables
/* None */

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


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

    // Process
    long dp[30+1]; /* 카탈란 수 */
    memset(dp, 0, sizeof(dp));
    dp[0] = 1; /* (W,H) 가 0 쌍일 때 가능한 문자열의 개수는 빈 문자열 1가지 */
    dp[1] = 1;
    dp[2] = 2;
    dp[3] = 5;
    dp[4] = 14;
    for (int i=5; i<=30; i++) {
        for (int j=0; j<i; j++) {
            dp[i] += dp[j] * dp[i-1-j];
        }
    }

    // Set up : Input
    while (true) {
        int N; cin >> N;
        if (N == 0) break;

        // Control : Output
        cout << dp[N] << endl;
    }
}

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

0개의 댓글