ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 고고학 최고의 발견
    코딩 테스트/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()
    반응형
Designed by Tistory.