[ 백준 ] 3019 / 테트리스

金弘均·2021년 9월 16일
0

Baekjoon Online Judge

목록 보기
190/228
post-thumbnail

# Appreciation

/*
 * Problem :: 3019 / 테트리스
 *
 * Kind :: Simulation
 *
 * Insight
 * - 블록과 바닥 사이에 채워져있지 않은 칸이 생기면 안된다
 *   + 블록과 바닥이 접할 때 빈칸이 있으면 안된다
 *     # 바닥의 모양으 고정되어있으니
 *       떨어지는 블록의 가장 아랫부분의 모양에 따라 유효한 방법인지 결정된다
 *       -> 떨어지는 블록이 몇번째 블록인지와 어디로 떨어지는지
 *          그리고 그 블록이 회전함으로써 가능한 블록의 가장 아랫부분의 모양을 고려하며
 *          블록을 놓는 서로 다른 방법의 수를 구하자
 *
 * - 5번 블록을 예로 들어보자
 *   +  ㅁ
 *     ㅁㅁㅁ
 *     모양으로 떨어지는 경우
 *     바닥의 3칸 연속 높이가 같아야만 유효한 방법이다
 *   +  ㅁ
 *     ㅁㅁ
 *      ㅁ
 *     모양으로 떨어지는 경우
 *     바닥의 첫번째 칸은 두번째 칸보다 높이가 1 커야만 유효한 방법이다
 *   + ㅁ
 *     ㅁㅁ
 *     ㅁ
 *     모양으로 떨어지는 경우
 *     바닥의 두번째 칸은 첫번째 칸보다 높이가 1 커야만 유효한 방법이다
 *   + ㅁㅁㅁ
 *      ㅁ
 *     모양으로 떨어지는 경우
 *     바닥의 첫번째 칸과 세번째 칸은 두번째 칸보다 높이가 1 커야만 유효한 방법이다
 *     # 이는 차례대로 바닥의 모양을
 *       {0, 0, 0}
 *       {1, 0}
 *       {0, 1}
 *       {1, 0, 1}
 *       로 패턴화 시킬 수 있다
 *       -> 이 패턴을 가지고 블록이 떨어지고 있는 위치에서
 *          바닥의 모양이 위 패턴에 부합하는지 확인하면 유효한 방법인지 알 수 있다
 *
 * - 바닥이 현재
 *   2 1 1 1 0 1 이고
 *   바닥의 모양이 {1, 0, 1} 의 패턴에 부합해야 한다고 하자
 *   + 인덱스 0 인 위치에 블록이 떨어질 때,
 *     그 위치의 바닥 모양 2 1 1 은 {1, 0, 1} 패턴에 부합하는가?
 *     (2-1, 1-0, 1-1) => (1, 1, 0) 으로 부합하지 않는다
 *     # 패턴은 현재 가장 높이가 낮은 칸 기준
 *       그 칸을 기준으로 높이가 높은 다른 칸에 양수가 들어가는 방식이므로
 *       이 양수를 그 칸의 바닥높이에서 빼줌으로써
 *       블록이 바닥에 접하는 가장 낮은 높이를 구할 수 있다
 *       여기서 모든 칸이 위 높이와 같을 때 유효한 방법이라는 것을알 수 있다
 *       위 원리를 이용해서 하나씩 검사하면 된다
 *       -> 인덱스 1 인 위치의 바닥 모양 1 1 1 은 {1, 0, 1} 패턴에 부합하는가?
 *          인덱스 2 인 위치의 바닥 모양 1 1 0 은 {1, 0, 1} 패턴에 부합하는가?
 *          인덱스 3 인 위치의 바닥 모양 1 0 1 은 {1, 0, 1} 패턴에 부합하는가?
 *
 * Point
 * - 사실...
 *   그냥 각 경우에 대해 하나하나 코딩하는 것이
 *   좀 하드코딩이긴 하지만 확실한 방법이고 더 쉬울 수도 있다
 *   + 이전에는 하드코딩으로 풀었기에 좀더 효율적인 다른 방식으로 풀고 싶기도 했고
 *     # 바닥의 모양만 알면 블록의 번호와 상관없이 유효한지 알 수 있기 때문에
 *       이번에는 위처럼 풀었다 :)
 */

# Code

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


#include <iostream>
#include <vector>

using namespace std;

#define endl '\n'

// Set up : Global Variables
int C, P;
vector<int> G;

// Set up : Functions Declaration
int cases(vector<int> pattern);


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

    // Set up : Input
    cin >> C >> P;
    G.resize(C);
    for (int i=0; i<C; i++)
        cin >> G[i];

    // Process
    int ans = 0;
    switch (P) {
        case 1: /* 1번 블록 */
            /* 1번 블록에서 유효한 바닥 모양의 패턴들 */
            ans += cases({0});
            ans += cases({0, 0, 0, 0});
            break;

        case 2: /* 2번 블록 */
            /* 2번 블록에서 유효한 바닥 모양의 패턴들 */
            ans += cases({0, 0});
            break;

        case 3: /* 3번 블록 */
            /* 3번 블록에서 유효한 바닥 모양의 패턴들 */
            ans += cases({0, 0, 1});
            ans += cases({1, 0});
            break;

        case 4: /* 4번 블록 */
            /* 4번 블록에서 유효한 바닥 모양의 패턴들 */
            ans += cases({1, 0, 0});
            ans += cases({0, 1});
            break;

        case 5: /* 5번 블록 */
            /* 5번 블록에서 유효한 바닥 모양의 패턴들 */
            ans += cases({0, 0, 0});
            ans += cases({1, 0});
            ans += cases({0, 1});
            ans += cases({1, 0, 1});
            break;

        case 6: /* 6번 블록 */
            /* 6번 블록에서 유효한 바닥 모양의 패턴들 */
            ans += cases({0, 0, 0});
            ans += cases({0, 0});
            ans += cases({0, 1, 1});
            ans += cases({2, 0});
            break;

        case 7: /* 7번 블록 */
            /* 7번 블록에서 유효한 바닥 모양의 패턴들 */
            ans += cases({0, 0, 0});
            ans += cases({0, 0});
            ans += cases({1, 1, 0});
            ans += cases({0, 2});
            break;

        default: throw;
    }

    // Control : Output
    cout << ans << endl;
}

// Helper Functions
int cases(vector<int> pattern)
/* 바닥 모양의 패턴이 들어왔을 때 유효한 서로 다른 방법의 수를 반환 */
{
    int cnt = 0; /* 유효한 서로 다른 방법의 수 */
    int L = pattern.size(); /* 블록과 접하는 바닥의 길이 = 떨어지는 블록의 가로 길이 */
    for (int i=0; i<=C-L; i++) { /* 인덱스 i 인 위치에 블록이 떨어질 때 */
        /* 편의상 고려할 G[i] ~ G[i+L-1] 의 바닥을 sub 라는 Vector 로 만듦 */
        vector<int> sub(G.begin()+i, G.begin()+i+L);
        /* 첫번째 칸의 패턴과 바닥의 높이를 통해 블록이 바닥에 접하는 가장 낮은 높이를 구함 */
        int h = sub[0] - pattern[0];
        if (h < 0) continue; /* 높이는 음수일 수 없음 */
        bool isValid = true; /* 현재 떨어지는 방식이 유효한지 여부 */
        for (int j=1; j<L; j++) { /* 두번째 칸부터 차례로 높이 검사 */
            /* 현재 칸 기준 패턴과 바닥의 높이를 통해 구한 높이가
             * 위에서 구한 높이와 같지 않으면 */
            if (h != sub[j] - pattern[j]) {
                isValid = false; /* 현재 떨어지는 방식이 유효하지 않음 */
                break;
            }
        }
        /* 현재 떨어지는 방식이 유효하면 방법의 수 증가 */
        if (isValid) { cnt++; }
    }
    return cnt;
}
profile
이런 미친 게임을 봤나! - 옥냥이

0개의 댓글