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

2025년 3월 9일 일요일 – 서울 생활 145주차

출장 그간의 출장들은 Summit 또는 온보딩이라, 부담도 없고 일정이 빡세지도 않았던 반면 이번 출장은 정말…

5일 ago

2025년 2월 23일 일요일 – 서울 생활 143주차

출장 오랜만에 출장을 간다. 미국으로 가는 출장은 항상 힘들다. 비행시간은 10시간에서 12시간. 도착해서 시차 극복도…

3주 ago

2025년 2월 16일 일요일 – 서울 생활 142주차

건강검진 재작년 말인가 작년 1월이었나, 인생 최악의 건강검진 결과를 받고서 다짐했었다. 2025년 건강검진에서는 완벽한 검사결과를…

4주 ago

2025년 2월 2일 일요일 – 서울 생활 140주차

운동 계획 운동과 식단과 피부관리 동시에 하니 시너지가 엄청나다. 변화가 눈에 보일 정도로 빠르다보니, 성취감도…

1개월 ago

2025년 1월 26일 일요일 – 서울 생활 139주차

아직 작성 날짜보다 앞이지만, 지금밖에 시간이 없을 것 같아 오늘 기록을 남긴다. 합격왕 합격왕 광고는…

2개월 ago

2024년 회고

조금 늦은 회고를 통해 작년 한 해 있었던 일들을 돌아보고, 2025년은 어떻게 살아갈지 생각해보자 영주권…

2개월 ago