[백준] 감시(15683) - python

당고짱·2023년 4월 23일
0

coding-test

목록 보기
50/50
post-thumbnail

✏️ 문제

스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감시할 수 있는 방법은 다음과 같다.

1번 CCTV는 한 쪽 방향만 감시할 수 있다. 2번과 3번은 두 방향을 감시할 수 있는데, 2번은 감시하는 방향이 서로 반대방향이어야 하고, 3번은 직각 방향이어야 한다. 4번은 세 방향, 5번은 네 방향을 감시할 수 있다.

CCTV는 감시할 수 있는 방향에 있는 칸 전체를 감시할 수 있다. 사무실에는 벽이 있는데, CCTV는 벽을 통과할 수 없다. CCTV가 감시할 수 없는 영역은 사각지대라고 한다.

CCTV는 회전시킬 수 있는데, 회전은 항상 90도 방향으로 해야 하며, 감시하려고 하는 방향이 가로 또는 세로 방향이어야 한다.

지도에서 0은 빈 칸, 6은 벽, 1~5는 CCTV의 번호이다. 위의 예시에서 1번의 방향에 따라 감시할 수 있는 영역을 '#'로 나타내면 아래와 같다.

CCTV는 벽을 통과할 수 없기 때문에, 1번이 → 방향을 감시하고 있을 때는 6의 오른쪽에 있는 칸을 감시할 수 없다.

위의 예시에서 감시할 수 있는 방향을 알아보면 아래와 같다.

CCTV는 CCTV를 통과할 수 있다. 아래 예시를 보자.

위와 같은 경우에 2의 방향이 ↕ 3의 방향이 ←와 ↓인 경우 감시받는 영역은 다음과 같다.

사무실의 크기와 상태, 그리고 CCTV의 정보가 주어졌을 때, CCTV의 방향을 적절히 정해서, 사각 지대의 최소 크기를 구하는 프로그램을 작성하시오.

🎈 입력형식

첫째 줄에 사무실의 세로 크기 N과 가로 크기 M이 주어진다. (1 ≤ N, M ≤ 8)

둘째 줄부터 N개의 줄에는 사무실 각 칸의 정보가 주어진다. 0은 빈 칸, 6은 벽, 1~5는 CCTV를 나타내고, 문제에서 설명한 CCTV의 종류이다.

CCTV의 최대 개수는 8개를 넘지 않는다.

🎈 출력형식

첫째 줄에 사각 지대의 최소 크기를 출력한다.

🎈 입출력 예

<입력>
4 6
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 6 0
0 0 0 0 0 0
<출력>
20

👩‍💻 내 코드 - dfs + 백트레킹

import sys
import copy

n, m = map(int, input().split())
arr = [list(map(int, sys.stdin.readline().split())) for _ in range(n)]
dy = [0, 0, 1, -1]
dx = [1, -1, 0, 0]
answer = 2134567890

cctv_list = []
for i in range(n):
    for j in range(m):
        if arr[i][j] != 0 and arr[i][j] != 6:
            cctv_list.append([arr[i][j], i, j])

cctv_type = [
    [],
    [[0], [1], [2], [3]],
    [[0, 1], [2, 3]],
    [[0, 2], [0, 3], [1, 2], [1, 3]],
    [[0, 1, 2], [0, 1, 3], [1, 2, 3], [0, 2, 3]],
    [[0, 1, 2, 3]]
]

def fill(arr, dir, y, x):
    for i in dir:
        ny, nx = y, x
        while 1:
            ny += dy[i]
            nx += dx[i]
            if ny < 0 or nx < 0 or ny >= n or nx >= m:
                break
            if arr[ny][nx] == 6:
                break
            arr[ny][nx] = 7

def dfs(now, arr):
    global answer

    if now == len(cctv_list):
        cnt = 0
        for i in range(n):
            for j in range(m):
                if arr[i][j] == 0:
                    cnt += 1
        answer = min(answer, cnt)
        return

    tmp_arr = copy.deepcopy(arr)
    cctv_num, y, x = cctv_list[now]

    for i in cctv_type[cctv_num]:
        fill(tmp_arr, i, y, x)
        dfs(now + 1, tmp_arr)
        tmp_arr = copy.deepcopy(arr)

dfs(0, arr)
print(answer)

💡 새롭게 배운 것

  • 파이썬 mutable, immutable 객체
    • mutable(변경되는 객체) : list, set, dictionary
    • immutable(변경되지 않는 객체) : int, float, tuple, str, bool
  • 파이썬 deepcopy
    • immutable 객체들은 얕은 복사, 깊은 복사 상관없다.(무조건 참조가 변경됨)
    • mutable 객체들은 얕은 복사, 깊은 복사 구분해야 한다.
      • '='와'[:]'은 얕은 복사
      • copy의 deepcopy는 깊은 복사 (주소 값이 달라진다.)
profile
초심 잃지 말기 🙂

0개의 댓글