[백준 2133] 타일 채우기(Java)

최민길(Gale)·2023년 1월 14일
1

알고리즘

목록 보기
12/172

문제 링크 : https://www.acmicpc.net/problem/2133

이 문제는 dp 문제입니다. dp 문제이니만큼 문제에서 요구하는 점화식을 잘 세우면 쉽게 풀 수 있습니다.

우선 홀수 개수일 경우에는 타일을 전부 채울 수 없기 때문에 0의 경우의 수를 가집니다. 그렇다면 짝수 개수를 알아보아야 할텐데, 먼저 2개일 때 가능한 경우의 수를 살펴보면

  1. 맨 아래에 가로 블록이 있는 경우
  2. 맨 위에 가로 블록이 있는 경우
  3. 모두 가로 블록이 있는 경우

로 3가지입니다. 그렇다면 4개일 때는 가능한 경우의 수가 어떻게 될까요? 우선 기존 2개일 때의 경우의 수에서 3만큼 곱한, 즉 새롭게 2칸의 블록을 채우는 경우의 수가 있겠습니다. 하지만 여기서 끝이 아닙니다. 맨 아래에 모두 가로 블록이 있는 경우, 맨 위에 모두 가로 블록이 있는 경우 총 2가지의 경우의 수가 추가됩니다. 따라서 4칸일 경우 다음의 점화식을 얻을 수 있습니다.

dp[4] = dp[2]*3 + 2;

그렇다면 6개일 때는 어떨까요? 우선 기존 4개일 떄의 경우의 수에서 3만큼 곱한 경우의 수가 존재하며, 맨 아래와 맨 위에 모두 가로 블록이 있는 2개의 경우의 수도 존재합니다. 하지만 여기서 한 가지 더 있습니다. 바로 이전 단계, 즉 4개를 채우는 경우의 수 중 맨 아래와 맨 위를 모두 가로 블록으로 채우는 2가지의 특수 경우의 수일 경우 칸을 채우는 방법이 남아있는데, 이 부분은 해당 범위를 뺀 나머지, 즉 2칸을 채우는 경우의 수를 제일 왼쪽과 제일 오른쪽 2번을 곱한 값을 추가해주면 됩니다. 그렇다면 점화식은 다음과 같습니다.

dp[6] = dp[4]3 + 2 + dp[2]2

그렇다면 8개 이상일 때, 즉 n일 때는 어떨까요? 기존 경우의 수에서 3만큼 곱한 경우의 수와, 맨 아래, 맨 위 모두 가로 블록이 있는 2가지 경우까지는 똑같습니다. 하지만 특수 경우의 수가 2칸에서부터 n-4 칸까지 양 끝을 채우는 경우의 수도 지속적으로 더해주어야 합니다. 따라서 최종 점화식은 다음과 같습니다.

dp[n] = dp[n-2]3 + 2 + (dp[2]2 + dp[4]2 + ... + dp[n-4]2)

다음은 코드입니다.

import java.util.*;
import java.io.*;

public class Main{

    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());

        int[] res= new int[31];

        // N이 홀수일 경우 완벽하게 채울 수 있는 경우의 수 없으므로 0 출력
        res[0] = 1;
        res[2] = 3;

        for(int i=4;i<=N;i++){
            // N이 홀수일 경우 완벽하게 채울 수 있는 경우의 수 없으므로 0 출력
            if(i%2==1) res[i] = 0;
            else{
                // 2블록 당 3개의 경우의 수 존재 = 3
                // 4블록일 경우 새로 추가된 3개의 경우의 수
                // + 2개의 추가 경우의 수(맨 위와 맨 아래에 가로 블록만 놓는 경우) 발생
                // : dp[4] = dp[2]*3 + 2;
                //
                // 6블록일 경우 새로 추가된 3개의 경우의 수
                // + 2개의 추가 경우의 수(맨 위와 맨 아래에 가로 블록만 놓는 경우)
                // + 4블록일 경우의 2가지의 특수 경우의 수 발생 시 양 끝에 2블록 놓을 수 있는 경우의 수의 2배 발생
                // : dp[6] = dp[4]*3 + 2 + dp[2]*2
                //
                // n 블록일 경우 새로 추가된 3개의 경우의 수
                // + 2개의 추가 경우의 수(맨 위와 맨 아래에 가로 블록만 놓는 경우)
                // 4블록부터 n-2블록일 경우의 2가지의 특수 경우의 수 발생 시 양 끝에 n-4 블록 놓을 수 있는 경우의 수의 2배 발생
                // : dp[n] = dp[n-2]*3 + 2 + (dp[2]*2 + dp[4]*2 + ... + dp[n-4]*2)

                // 기존 경우의 수와 독립적으로 추가되는 부분 : 새롭게 추가되는 부분
                // + 2개의 추가 경우의 수(맨 위와 맨 아래에 가로 블록만 놓는 경우)
                res[i] = res[i-2]*3 + 2;

                // 4블록부터 n-2블록일 경우의 2가지의 특수 경우의 수 발생 시 양 끝에 n-4 블록 놓을 수 있는 경우의 수의 2배
                for(int j=4;j<i;j+=2){
                    res[i] += res[i-j]*2;
                }
            }
        }
        System.out.println(res[N]);
    }
}

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글