[백준] 2xN 타일링

Jun 1, 2019


문제 요약


링크 : 2xN 타일링

세로 2, 가로 N인 2xN 직사각형 타일틀이 있다.
이 직사각형 타일 틀 안에, 세로 1, 가로 2 인 1x2 직사각형 (쉽게 생각해서 ‘ㅡ’ 이런 형태) 과 세로 2, 가로 1 인 2x1 직사각형 (‘|’) 을 채워 넣을 때, 채워 넣는 방법의 개수를 구해라.

풀이


처음에는 2행 N열 짜리 배열안에 직접, ‘1x2 & 2x1’ 을 채워 넣으면서 찾으려 했는데, 너무 복잡해 지고 시간도 많이 소요된다.

하다보니, 규칙성을 찾게 되었다.
2xN 직사각형을 타일틀이 있다면, 이 타일틀에 구성할 수 있는 타일 모양의 개수는, ‘2x(N-1)’의 방법의 수 + ‘2x(N-2)’의 방법의 수 라는 점화식이 도출 된다.

해당 방법으로 나는, List 를 만들어서 N 만큼의 배열을 만들어 가면서 찾았다. 헌데, 문제에서 N 을 입력받고 ‘2xN’ 이 경우에만 찾는 것이어서 상관은 없었지만, 여러개의 N 이 주어진다면, 배열에 이미 N 까지의 수를 다 생성해 놓고 접근하는 것이 좋을 듯 하다.

진행을 하다보면, 경우의 수가 int 범위를 초과하는 경우가 생긴다.
그 경우, 사칙연산에서 오류가 발생할 수 있기 때문에, ‘BigInteger’로 하나씩 선언해 주면서 처리했다.

Code


package backjoon;

import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;

/*
잘 생각해 보자.

타일을 일일이 다 깔아가면서 세기가 빡세다.
규칙이 있다.

예를들어, 1차원 배열이 있다고 생각하자.
쭉 이어져 있을 것이고, 세로 타일은 '1'만큼 자리를 차지 할 테고, 가로 타일은 '2'만큼 자리를 차지 할 것이다.

구체적으로 배열의 크기를 생각해보면 배열의 크기가 1일 떄 들어갈 수 있는 타일은
[1] 뿐이 없다.

 */
public class Tiling {

  private static final Scanner scan = new Scanner(System.in);

  private void solve3(int tileLength) {
    List<BigInteger> dp = new LinkedList<>(); // 어차피 타일의 길이는 '1000'으로 한정 되어 있기 때문에, 'new int[1000]' 으로 만들어도 가능.

    dp.add(BigInteger.valueOf(1));
    dp.add(BigInteger.valueOf(2));
    dp.add(BigInteger.valueOf(3));

    if (tileLength > 3) {
      for (int i = 1; i <= tileLength - 3; i++) {

        BigInteger tmp1 = dp.get(i);
        BigInteger tmp2 = dp.get(i + 1);

        BigInteger sum = tmp1.add(tmp2);

        dp.add(sum);
      }
    }

    BigInteger factor = BigInteger.valueOf(10007);

    // 타일의 열은 '1' 부터 증가하고(2x1 -> 2x2 ... ), 해당 타일의 갯수를 저장하는 배열은 인덱스가 '0' 부터 시작.
    int N = (tileLength - 1);

    BigInteger result = dp.get(N).mod(factor);

    System.out.println(result);
  }

  // 점화식, int 범위를 넘어가는 수가 list 에 들어갈 경우 결과가 달라 질 수 있다.
  private void checker(int tileLength) {
    int[] eachTileCntArr = new int[1001];

    eachTileCntArr[1] = 1;
    eachTileCntArr[2] = 2;
    eachTileCntArr[3] = 3;

    if (tileLength > 3) {

      // 'N - 1' 까지의 타일링을 알면, 길이가 'N'인 타일링을 알 수 있다.
      int length = (tileLength - 1);

      // '2x4' 길이의 타일링 부터 구해야 한다.
      int start = 4;

      for (int i = 2; i < length; i++) {

        // int 범위를 넘어가는 숫자가 발생한다.
        if (eachTileCntArr[i] > eachTileCntArr[i + 1]) {
          System.out.format("int 범위를 넘어가는 부분 N: %d / 해당하는 부분의 값 N: %d, N + 1: %d \n",
                  start, eachTileCntArr[i], eachTileCntArr[i + 1]);
        }
        eachTileCntArr[start++] = (eachTileCntArr[i] + eachTileCntArr[i + 1]);
      }

    }
  }

  // Stack, 시간 초과
  private void solve(int tileLength) {
    int count = 0;

    int factor = 10007;

    Stack<Integer> stack = new Stack<>();

    stack.push(tileLength);

    while (stack.iterator().hasNext()) {
      int temp = stack.pop();

      for (int i = 1; i < 3; i++) {
        int result = (temp - i);

        if (result < 0) continue;

        if (result == 0) count++;
        else stack.push(result);
      }
    }

    System.out.println((count % factor));
  }

  public static void main(String[] args) {
    int N = scan.nextInt();

    if (N < 0 || N > 1000) {
      System.out.println("입력 값 오류 ( 1<= N <= 1000)");
      return;
    }

    new Tiling().checker(N);
  }
}