Coding Test

[프로그래머스] 3주차_퍼즐 조각 채우기

상상쓰 2021. 8. 18. 13:39

https://programmers.co.kr/learn/courses/30/lessons/84021

 

코딩테스트 연습 - 3주차

[[1,1,0,0,1,0],[0,0,1,0,1,0],[0,1,1,0,0,1],[1,1,0,1,1,1],[1,0,0,0,1,0],[0,1,1,1,0,0]] [[1,0,0,1,1,0],[1,0,1,0,1,0],[0,1,1,0,1,1],[0,0,1,0,0,0],[1,1,0,1,1,0],[0,1,0,0,0,0]] 14 [[0,0,0],[1,1,0],[1,1,1]] [[1,1,1],[1,0,0],[0,0,0]] 0

programmers.co.kr

 

위클리 챌린지 3주차 문제이다. 갑자기 난이도가 올라갔다.

 

문제의 첫 번째 입출력 예로 설명을 하자면,

game_board =

[1, 1, 0, 0, 1, 0]
[0, 0, 1, 0, 1, 0]
[0, 1, 1, 0, 0, 1]
[1, 1, 0, 1, 1, 1]
[1, 0, 0, 0, 1, 0]
[0, 1, 1, 1, 0, 0]

이고 0으로 인접한 것들끼리 묶인 블록을 찾는다. (table 에서는 1)

 

예제의 도형은 6개로,

[0, 0, 1]  [0]  [0, 0]  [1, 0, 1]  [1, 0]  [0]
[1, 0, 1]  [0]  [0, 1]  [0, 0, 0]  [0, 0]
[1, 0, 0]

 

BFS 알고리즘을 통하여 위의 도형을 찾을 수 있다.

정확하게는 BFS 알고리즘을 이용하여 해당하는 도형의 [가장 왼쪽, 가장 위], [가장 오른쪽, 가장 밑] 좌표를 구한다.

l, t, r, b = min(left_index), min(top_index), max(right_index), max(bottom_index)

그리고 도형의 블록 개수, answer 에 더해질 값인 count 를 구하여 piece.append([l, t, r, b, count]) 를 한다. 

 

예로 piece 의 첫 번째 행은 [0, 2, 2, 4, 5] 이다. 이를 활용하여 2차원 배열인 game_board 를 slicing 하면, 도형이

[row[t:b+1] for row in game_board[l:r+1]] = [row[2:4+1] for row in game_board[0:2+1]] =

[0, 0, 1]
[1, 0, 1]
[1, 0, 0] 와 0의 개수가 5라는 것을 알 수 있다.

 

table 도 마찬가지로 도형을 구하여 piece2에 담아 piece와 piece를 비교하여 퍼즐 조각을 채울 수 있으면 count 를 answer 에 더해 answer 을 반환하면 된다.

 

piece2 에 있는 도형은 회전을 할 수 있으므로 rotate90 함수를 구현하여 4번의 비교를 한다. (0도, 90도, 180도, 270도)

piece2 에 있는 도형 또는 회전한 도형이 piece 에 있는 도형과 가로, 세로 길이와 count 가 일치하면 하나씩 비교하여 더했을 때 모든 값이 1이면 퍼즐 조각을 채울 수 있다고 판단할 수 있다. 퍼즐 조각을 채우면 사용된 도형이므로 piece2 에서 remove 해준다.

 

from collections import defaultdict, deque

def solution(game_board, table):
    answer = 0
    N = len(game_board)
    visit, visit2 = defaultdict(int), defaultdict(int)
    queue, queue2 = deque(), deque()
    d = [(1, 0), (0, 1), (-1, 0), (0, -1)]
    piece, piece2 = [], []
        
    for i in range(N):
        for j in range(N):
            if game_board[i][j] == 0:
                if visit[(i, j)] == 0:
                    l, t, r, b = i, j, i, j
                    visit[(i, j)] = 1
                    queue.append([i, j])
                    count = 1
                    
                    while queue:
                        x, y = queue.popleft()
                        
                        for d1, d2 in d:
                            if 0 <= x + d1 < N and 0 <= y + d2 < N and game_board[x + d1][y + d2] == 0 and visit[(x + d1, y + d2)] == 0:
                                queue.append([x + d1, y + d2])
                                l = min(l, x + d1)
                                t = min(t, y + d2)
                                r = max(r, x + d1)
                                b = max(b, y + d2)
                                visit[(x + d1, y + d2)] = 1
                                count += 1
                    
                    piece.append([l, t, r, b, count])
            
            if table[i][j] == 1:
                if visit2[(i, j)] == 0:
                    l, t, r, b = i, j, i, j
                    visit2[(i, j)] = 1
                    queue2.append([i, j])
                    count = 1
                    
                    while queue2:
                        x, y = queue2.popleft()
                        
                        for d1, d2 in d:
                            if 0 <= x + d1 < N and 0 <= y + d2 < N and table[x + d1][y + d2] == 1 and visit2[(x + d1, y + d2)] == 0:
                                queue2.append([x + d1, y + d2])
                                l = min(l, x + d1)
                                t = min(t, y + d2)
                                r = max(r, x + d1)
                                b = max(b, y + d2)
                                visit2[(x + d1, y + d2)] = 1
                                count += 1
                    
                    piece2.append([l, t, r, b, count])
    
    for l, t, r, b, count in piece:
        for l2, t2, r2, b2, count2 in piece2:
            A = [row[t:b+1] for row in game_board[l:r+1]]
            B = [row[t2:b2+1] for row in table[l2:r2+1]]
            
            if count == count2:
                c = 0
                T = False
                
                while c < 4 and not T:
                    c += 1
                    n = len(A)
                    m = len(A[0])
                    
                    if n == len(B) and m == len(B[0]):
                        for i in range(n):
                            for j in range(m):
                                if A[i][j] + B[i][j] != 1: break

                                if i == n - 1 and j == m - 1:
                                    T = True
                                    break

                            if A[i][j] + B[i][j] != 1 or T: break
                    
                    B = rotate90(B)
            
                if T:
                    answer += count
                    piece2.remove([l2, t2, r2, b2, count2])
                    break
        
    return answer
        
def rotate90(key):
    n = len(key)
    m = len(key[0])
    
    key_ = [[0] * n for i in range(m)]
    
    for i in range(n):
        for j in range(m):
            key_[j][n-i-1] = key[i][j]
    
    return key_
    
    print(solution([[1, 1, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0], [0, 1, 1, 0, 0, 1], [1, 1, 0, 1, 1, 1], [1, 0, 0, 0, 1, 0], [0, 1, 1, 1, 0, 0]], [[1, 0, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0], [0, 1, 1, 0, 1, 1], [0, 0, 1, 0, 0, 0], [1, 1, 0, 1, 1, 0], [0, 1, 0, 0, 0, 0]])) # 14