코딩 테스트/Level 3

고고학 최고의 발견

컴닥 2022. 12. 11. 22:03
반응형

https://school.programmers.co.kr/learn/courses/30/lessons/131702

고고학 최고의 발견 연습문제 Lv. 3 62명 4%

 

요약

첫 번째 줄이 결정되면, 두 번째 줄도 1가지 케이스로 결정된다. 
첫 번째 줄의 모든 케이스를 전수조사(BF)한다. 

 

꽤 힘들게 풀었다 ㅠ,.ㅠ 레벨 3 맞음?

첫 줄이 결정되면 다음 줄의 답도 결정된다. 
(상하좌우 방향은 관계 없다.)

첫 줄이 3, 0, 3, 0 으로 결정되어 있다면
다음 줄은 1, 0, 1, 0 회전하는 케이스만 존재한다.  

그럼 첫 줄은 어떻게 결정할까? 
첫 줄과 두 번째 줄은 서로 영향을 주기 때문에.. 
복잡한 문제다. 

첫 줄을 3, 3, 3, 0 에서 1번 턴을 해서
0, 0, 0, 0 으로 만들었더라도 
두 번째 줄의 상황에 따라 최적의 해가 아닐 수가 있다. 

가장 얄팍한 해결책은
첫 번째 줄과 두 번째 줄의 '모든' 케이스를 체크해서
첫 번째 줄을 0, 0, 0, 0 으로 만드는 최소의 회전 수를 찾는 방법이다. 

경우의 수는 4**8 = 65536, 코드는 작동했으나,
당연히 시간제한. 

코드가 작동하는 걸 확인했으니
이것을 어떻게 최적화할까..

첫 줄의 모든 경우의 수를 뽑으면 4**4 = 256
이 정도는 BF(전수조사) 해볼 만한 숫자다. 

첫 줄의 모든 케이스를 만든 뒤,
이를 바탕으로 다음 줄을 결정하고.
마지막 줄까지 결정되면
전체가 0이 맞는지 확인하고
턴 수의 최솟값을 모으면 답이 나온다. 

첫 줄의 성공 여부을 먼저 확인하지 않고,
마지막으로 미루는 것이 오히려 간단하다.  

다음 줄 결정에
복잡한 연산이 필요 없기 때문에
가능한 시나리오이다. 

from copy import deepcopy


def solution(clocks):
    def mask_generator():  # 0 번 라인의 시계 바늘을 얼마나 돌릴 지 모든 경우를 만듬
        for i in range(4 ** length):
            mask = []
            while i:
                mask.append(i % 4)
                i //= 4
            yield mask + [0] * (length - len(mask))

    def check_all(board):  # 모든 시계의 0시 체크
        for row in board:
            for each in row:
                if each != 0:
                    return False
        return True

    def turn_clock(y, x, time, board):  # 시계 바늘을 돌려준다.
        for dy, dx in ((-1, 0), (0, -1), (0, 0), (0, 1), (1, 0)):
            if 0 <= (ny := y + dy) < length and 0 <= (nx := x + dx) < length:
                board[ny][nx] = (board[ny][nx] + time) % 4

    def turn_line(line_num, board):  # 윗 줄의 시계 바늘에 따라 아래 시계의 바늘 회전
        turns = 0
        for index, each in enumerate(board[line_num - 1]):
            if each > 0:
                turn_num = 4 - each
                turn_clock(line_num, index, turn_num, board)
                turns += turn_num
        return turns

    def solve():
        min_turn = float("inf")
        for mask in mask_generator():  # 모든 경우를 만듬.
            board = deepcopy(clocks)  # 2차원 배열의 복사에 딥카피 필요. 
            for index, turn_num in enumerate(mask):  # mask를 board에 적용. 0번 줄 회전. 
                turn_clock(0, index, turn_num, board)  # 이거 삽질이다. 그냥 mask를 바로 1열에
            turns = sum(mask)  # mask의 합 = 지금까지 회전수
            for line_num in range(1, length):
                turns += turn_line(line_num, board)
            if check_all(board):  # 체크
                min_turn = min(min_turn, turns)
        return min_turn

    length = len(clocks)
    return solve()

코드를 조금 수정했다...

product 를 잊고 있었다. 
그리고 마지막 줄만 체크하면 되는데... 

from copy import deepcopy
from itertools import product


def solution(clocks):
    def check_row(row):  # 시계의 0시 체크
        for each in row:
            if each != 0:
                return False
        return True

    def turn_clock(y, x, time, board):  # 시계 바늘을 돌려준다.
        for dy, dx in ((-1, 0), (0, -1), (0, 0), (0, 1), (1, 0)):
            if 0 <= (ny := y + dy) < length and 0 <= (nx := x + dx) < length:
                board[ny][nx] = (board[ny][nx] + time) % 4

    def turn_line(line_num, board):  # 윗 줄의 시계 바늘에 따라 아래 시계의 바늘 회전
        turns = 0
        for index, each in enumerate(board[line_num - 1]):
            if each > 0:
                turn_num = 4 - each
                turn_clock(line_num, index, turn_num, board)
                turns += turn_num
        return turns

    def solve():
        min_turn = float("inf")
        for mask in product(range(4), repeat=length):  # 모든 경우를 만듬.
            board = deepcopy(clocks)  # 2차원 배열의 복사에 딥카피 필요. 
            for index, turn_num in enumerate(mask):  # mask를 board에 적용. 0번 줄 회전. 
                turn_clock(0, index, turn_num, board)
            turns = sum(mask)  # mask의 합 = 지금까지 회전수
            for line_num in range(1, length):
                turns += turn_line(line_num, board)
            if check_row(board[length - 1]):
                min_turn = min(min_turn, turns)
        return min_turn

    length = len(clocks)
    return solve()
반응형