[백준 1720] 타일 코드(Java)

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

알고리즘

목록 보기
13/172

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

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

우선 각 경우의 수에서 좌우 대칭인 타일 수를 A, 좌우 대칭이 아닌 타일 수를 B라고 했을 때 항상 A+2B의 경우의 수가 나타납니다. 따라서 중복 제거가 된 값인 A+B를 얻기 위해서는 좌우 대칭인 타일 수를 더한 후 절반으로 나누어서 문제를 풀면 됩니다.

그럼 먼저 중복 포함 모든 타일의 경우의 수, 즉 A+2B에 대해 먼저 구해보겠습니다. 각 경우는 크게 2가지입니다.

우선 현재 길이에서 1만큼 뺀 후 1만큼의 길이를 채우는 경우의 수입니다. 이 경우는 1X2 타일 1개를 채우는 경우밖에 없으므로 dp[n-1]과 같습니다.

이어서 현재 길이에서 2만큼 뺀 후 2만큼의 길이를 채우는 경우의 수입니다. 이 경우는 2X1, 2X2 2개의 경우가 존재하므로 dp[n-2]*2가 됩니다.

즉 중복 포함 모든 타일의 경우의 수 dp[n] = dp[n-1] + dp[n-2]*2 가 됩니다.

그럼 각 경우의 중복 타일 수를 체크해보겠습니다. 우선 홀수 번째 경우는 가운데 세로 막대기를 기준으로 대칭인 경우밖에 없습니다. 따라서 가운데 막대기 기준 절반의 길이에 해당하는 dp[n/2] 만큼 더한 후 절반으로 나눠주면 됩니다. 따라서

dp[n] = (dp[n] + dp[n/2])/2

가 됩니다.

짝수 번째 경우는 크게 2가지가 존재합니다. 우선 완벽한 좌우 대칭인 경우입니다. 이 경우는 위와 같이 절반의 길이에 해당하는 dp[n/2]를 더해주면 됩니다. 이어서 가운데에 길이가 2인 블록이 있는 경우입니다. 이는 현재 길이의 절반 만큼에서 1만큼(원래 길이가 2이기 때문에 절반으로 나누면 1로 됩니다) 뺀 길이의 값, 즉 dp[n/2 -1]만큼 더해주면 됩니다. 이 때 2X2, 2X1 2개의 경우가 존재하기 때문에 해당 값을 2배 해서 더해주면 됩니다. 따라서

dp[n] = (dp[n] + dp[n/2] + dp[n/2 -1]*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[] dp = new int[31];

        dp[0] = 1;
        dp[1] = 1;

        // 각 경우의 수에서 좌우 대칭인 타일 수를 A, 좌우 대칭이 아닌 타일 수를 B라고 하면
        // 기본적으로 A + 2B 의 경우의 수가 나타난다.
        // 따라서 좌우 대칭인 타일 수를 더해서 반으로 나눠주면 A+B를 구할 수 있다.
        //
        // 중복 포함 모든 타일의 경우의 수
        // 1. 현재 길이에서 2만큼 뺀 후 2만큼 채우는 경우 : 2*2, 2*1 총 2가지 존재 : dp[n-2]*2
        // 2. 현재 길이에서 1만큼 뺀 후 1만큼 채우는 경우 : 1*2 총 1가지 존재 : dp[n-1]
        // dp[n] = dp[n-2]*2 + dp[n-1]
        //
        // 홀수일 경우의 좌우 대칭 타일 수 : 가운데 세로 막대기 기준으로 대칭인 경우밖에 없음
        // 따라서 가운데 막대기 기준 절반의 길이에 해당하는 dp[n/2] 만큼 더한 후 절반으로 나눠주면 됨
        // dp[n] = (dp[n] + dp[n/2])/2
        //
        // 짝수일 경우의 좌우 대칭 타일 수
        // 1. 절반 길이만큼 좌우 대칭 : dp[n/2]
        // 2. 가운데에 길이가 2인 블록이 있는 경우 : 2*2, 2*1 총 2가지 존재
        // 이는 현재 길이의 절반 만큼에서 1만큼 뺀 길이의 값 : dp[n/2 - 1]*2
        // dp[n] = (dp[n] + dp[n/2] + dp[n/2 -1]*2

        for(int i=2;i<=N;i++){
            dp[i] = dp[i-1] + dp[i-2]*2;
        }

        if(N%2==1) System.out.println((dp[N] + dp[N/2])/2);
        else System.out.println((dp[N] + dp[N/2] + dp[N/2 -1]*2)/2);
    }
}

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

0개의 댓글

관련 채용 정보