Coding Interview

[코딩 인터뷰][HackerRank] Lego Blocks

※ 이 포스트는 HackerRank 문제인 Lego Blocks 해설을 담고 있습니다.
본 풀이는 Optimal하지 않을 수 있으며, HackerRank에서의 Submission 통과만을 보장합니다.


Lego Blocks

문제 출처 : https://www.hackerrank.com/challenges/lego-blocks/problem
카테고리 : DP
난이도 : Medium (50 point)


문제 설명

가로, 세로, 높이를 가진 직육면체가 4종류 있다. 높이와 세로는 모두 1로 동일하고 가로만 1, 2, 3, 4로 4종류가 있다. 이 블럭을 쌓아서 직육면체 벽을 세우려고 한다. 벽의 세로 길이는 1이라고 한다. 따라서 그냥 벽을 입체가 아니라 면이라고 생각하는게 더 편할 것 같다.

위와 같이 좋은 레이아웃과 나쁜 레이아웃을 구분짓는 기준은 하나다. 오른쪽 Bad layout과 같이 세로로 벽을 완전히 자를 수 있는 직선이 존재한다면 나쁜 레이아웃이다. 즉, 이어진 세로선이 벽의 세로길이와 완전히 같은 것을 나쁘다고 생각하면 된다.

만들어야 할 벽의 세로, 가로 (N, M)이 주어질 때 만들 수 있는 좋은 레이아웃의 수를 구하여라. 수가 너무 크다면 10^9 + 7로 나눈 나머지를 구하여라.


문제 풀이

우선 레이아웃의 좋고 나쁨을 구별하지말고, 벽을 구성할 수 있는 전체 가짓수를 알아보자. 한 줄을 완성하는 가짓수를 X라고 한다면, N개의 줄은 높이마다 독립적이므로 X^N이 정답이다.

block[i]를 i + 1칸짜리 한 줄을 만드는 경우의 수라고 하자. block[i]는 어떻게 만들어지는가. block[i – 4]에서 4칸짜리 블록을 추가하거나, block[i – 3]에서 3칸짜리 블록을 추가하거나하는 식이다. 즉, block[i] = block[i – 4] + block[i – 3] + block[i – 2] + block[i – 1] 이다. 물론 i – 4나 i – 3같은 값은 음수가 되어선 안된다.

block[i]별로 block[i]^N을 구한다. N을 2진수로 생각해서 곱하는 전략을 취한다. 예를 들어 3^5는 3^(1+0+4)와 같고, 3^13은 3^(1+0+4+8)과 같다. 13은 2진수로 1101이다. 매 Iteration마다 N을 2로 나눈 나머지가 1이면, 현재의 factor를 곱한다. factor는 매 Iteration마다 자기 자신의 제곱이된다.

이 과정을 거친 후 block[i]는 (N, i + 1)의 크기의 벽을 완성할 수 있는 경우의 수가 된다.

이제 이 중에서 나쁜 레이아웃을 제거함으로써, 좋은 레이아웃의 수만 남길 것이다. 나쁜 레이아웃을 최초로 세로로 잘리는 지점을 가지고 분류한다고 생각해보자. 만약 가로가 4칸인 벽이라고 한다면, 최초로 잘리는 지점이 1또는 2또는 3번째 칸일 것이다. 그렇다면, 잘리는 세로를 기준으로 왼쪽은 반드시 좋은 레이아웃 (그렇지 않다면, 그 지점보다 왼쪽에 세로로 잘리는 지점이 있다는 말이므로 가정에 위배된다.)이고 오른쪽은 아무 레이아웃이나 있으면 된다.

가로가 i + 1인 벽의 좋은 레이아웃의 수를 ans[i]라고 하자.

ans[3] = block[3] (전체 가능한 레이아웃 수) – ans[0] * block[2] – ans[1] * block[1] – ans[2] * block[0] 이다.


코드
#!/bin/python3

import os
import sys

#
# Complete the legoBlocks function below.
#
def legoBlocks(n, m):
    #
    # Write your code here.
    #
    mod = 10 ** 9 + 7
    block = [0] * m
    
    for i in range(min(m, 4)):
        block[i] = 1

    for i in range(m):
        for j in range(1, 5):
            if i + j < m:
                block[i + j] = (block[i + j] + block[i]) % mod
        x = n
        tmp = block[i]
        block[i] = 1
        while x > 0:
            if x % 2 == 1:
                block[i] = (block[i] * tmp) % mod
            tmp = (tmp * tmp) % mod
            x //= 2

    ans = [0] * m
    for i in range(m):
        ans[i] = block[i]
        for j in range(i):
            ans[i] = (ans[i] - ans[j] * block[i - 1 - j]) % mod
    return (ans[m - 1] + mod) % mod
        
if __name__ == '__main__':
    fptr = open(os.environ['OUTPUT_PATH'], 'w')

    t = int(input())

    for t_itr in range(t):
        nm = input().split()

        n = int(nm[0])

        m = int(nm[1])

        result = legoBlocks(n, m)

        fptr.write(str(result) + '\n')

    fptr.close()

코딩 인터뷰 – Lego Blocks

admin

Share
Published by
admin

Recent Posts

2024년 11월 3일 일요일 – 서울 생활 127주차

반복적인 일상 매일 아침 일어나 회사에 출근하고, 저녁을 먹고 돌아오는 일상의 반복이다. 주말은 가족을 보러…

2일 ago

2024년 10월 6일 일요일 – 서울 생활 123주차

Planning A well-structured plan has the following characteristics:   First, the ultimate vision you aim to…

1개월 ago

2024년 9월 29일 일요일 – 서울 생활 122주차

English The most common problem for English learners like myself is that we often use…

1개월 ago

2024년 9월 22일 일요일 – 서울 생활 121주차

추석 추석을 맞아 친가 외가 처가 어르신들을 모두 뵙고 돌아왔다. 아이가 태어난지는 좀 됐지만 한국에서…

1개월 ago

2024년 9월 8일 일요일 – 서울 생활 119주차

주말 이번주는 아내가 올라와서 주말을 함께 보냈다. 둘만 밥도 먹고 뮤지컬도 보고, 모임도 나가고 집에서…

2개월 ago

2024년 9월 1일 일요일 – 서울 생활 118주차

여행 지난주에 코로나에 걸리는 바람에 근 3주만에 가족을 만나고 왔다. 집에 있어봐야 시간도 안가고 할…

2개월 ago

This website uses cookies.