오늘의 문제 boj-15683

코변·2022년 11월 4일
0

알고리즘 공부

목록 보기
25/65

문제

풀이

이 문제는 주어진 입력의 갯수도 8개로 상당히 작고 cctv를 돌려서 얻을 수 있는 가능한 모든 상황을 고려해야하는 문제이므로 백트래킹으로 풀 수 있다.

cctv를 어떻게 효율적으로 잘 돌릴 것인가, 재귀함수의 로직을 어떻게 짤 것인가 이 두가지만 잘 고려하면 풀 수 있는 문제라고 생각한다. 물론 이게 무지막지하게 어렵다고 느껴지지만

우선 로직의 순서는 cctv의 갯수를 기준으로 경우의 수를 체크할 것이기 때문에(또한 매번 N*M 순회를 하는 것은 비효율적이므로) 따로 cctvs 라는 리스트에 추출해준다.

다 추출하고 나면 이제 count_blind_spot이라는 함수를 통해서 사각지대를 세어줄건데

그 전에 정해진 방향으로 blind_spot을 #으로 그려주는 paint_blind_spot함수를 통해 넘겨받은 direction이라는 weight값을 벽에 닿거나 리스트 바깥으로 벗어나기 전까지 더해주면서 0으로 되어있는 빈 값을 #으로 채워준다.

그렇게 경우의 수를 하나하나 스택에 쌓아주고 cctvs 리스트의 길이 만큼 depth가 깊어지면 한 사이클을 모두 돌았다는 의미기 때문에 0의 값의 갯수를 구해서 최소값 검사를 통해 업데이트를 해준다.

마지막으로 이 로직의 핵심은 copy.deepcopy를 통해 함수를 빠져나오면 coppied_cctv_map을 이전 값으로 돌려주는 것이라고 생각한다. for문 위에서 한 번 함수를 빠져나와서 한 번 선언해주면서 마치 백트래킹에서 visited나 is_used리스트를 선언해서 값을 넣어주고 함수가 끝나면 pop해주는 것처럼 사용했다.

from typing import List
import copy
import sys


def paint_blind_spot(row: int, col: int, direction:List[int], cctv_map:List[List[int]]) -> None:

    for d in direction:
        nrow, ncol = row, col
        while 0 <= nrow < N and 0 <= ncol < M and cctv_map[nrow][ncol] != 6:
            if cctv_map[nrow][ncol] == 0:
                cctv_map[nrow][ncol] = "#"
                
            nrow += directions[d][0]
            ncol += directions[d][1]
                
def count_blind_spot(depth: int, cctv_map: List[List[int]]) -> None:
    global answer

    if depth == len(cctvs):
        answer = min(answer , sum(map(lambda x: x.count(0), cctv_map)))
        return

    coppied_cctv_map = copy.deepcopy(cctv_map)
    row, col, cctv_type = cctvs[depth]

    for cctv_dir in cctv_direction[cctv_type]:

        paint_blind_spot(row, col, cctv_dir, coppied_cctv_map)

        count_blind_spot(depth+1, coppied_cctv_map)
        
        coppied_cctv_map = copy.deepcopy(cctv_map)


if __name__ == '__main__':
    sys.stdin = open('', 'r')
    N, M = map(int, input().split())
    cctv_map = [list(map(int, input().split())) for _ in range(N)]
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

    cctv_direction = [
        [],
        [[0], [1], [2], [3]], 
        [[0, 1], [2, 3]], 
        [[0, 2], [0, 3], [1, 2], [1, 3]],
        [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]], 
        [[0, 1, 2, 3]] 
    ]
    answer = int(10e9)

    cctvs = []
    for row_idx in range(N):
        for col_idx in range(M):
            if cctv_map[row_idx][col_idx] == 6:
                continue
            if cctv_map[row_idx][col_idx] != 0:
                cctvs.append((row_idx,col_idx, cctv_map[row_idx][col_idx]))
    count_blind_spot(0, cctv_map)

    print(answer)


피드백

바킹독님이 몇시간이 걸리든 한 번 풀어보라고 해서 오늘 4시간 정도 들여서 겨우 풀 수 있었다. 사실 deepcopy를 활용한 부분은 디버그 모드로 이래저래 끼워맞추다 우연히 얻은 결과니 사실상 풀었다고 보기 어렵다.

그리고 direction 부분은 다른 분의 코드를 참고 했는데 나는 저렇게 줄일 방법이 떠오르지 않아서 일일히 방향을 다 쳤는데 저 코드를 보면서 무릎을 탁 쳤다.

그래도 어제 h님의 조언대로 타입을 명시해 더 코드를 보기 쉽게 만들었다. 또한 최대한 명시적인 변수명을 선언하려고 노력했다.

요즘 문제 푸는게 좀 즐거워지기 시작했다. 머리는 좀 아프지만 점점 느는 것 같아서 기쁘다.

profile
내 것인 줄 알았으나 받은 모든 것이 선물이었다.

0개의 댓글