Yeon's 개발블로그

지식을 전파하는 개발자가 되고싶습니다.

Algorithms/Samsung

[python] BOJ 백준 15683: 감시

Dev.yeon 2022. 4. 13. 02:05

1. 문제  

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

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

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

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

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

2. 입력   

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

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

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

3. 출력   

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

4. 해결방법   

이 문제는 DFS과 브루트포스알고리즘을 사용하여 풀어야하는 문제이다. CCTV를 회전할 수 있기 때문에 이 회전 가능한 경우의 수를 모두 따져주어 사각지대를 탐색해야한다.(브루트포스) 또한 재귀적으로 깊이우선탐색을 시행하여 해당 경우에서의 최소값을 구해 업데이트를 해주어야한다.(DFS) CCTV배열에 담긴 순서대로 깊이우선탐색을 하되, 회전가능한 모든 경우의 수를 구해주어야 하기때문에 Mode 배열에 담긴 방향을 탐색하는 것까지 고려하여 깊이우선탐색을 진행해야한다. 보통의 문제라면 시간초과가 나겠지만, 이 문제에서는 CCTV의 최대개수를 8개로 제한했기 때문에 방향을 제일 많이 고려해야하는 4번 CCTV로만 이루어져있다고 하더라도 4^8의 시간복잡도를 가지기 때문에 시간초과가 나지는 않는다. 구현은 씨씨티비가 감시가 가능한 부분을 -1로 바꾸는 fill함수와 모든 경우의 수에 대해서 깊이우선탐색을 진행하는 dfs함수로 구성을 하였다.  

1) fill 함수: CCTV의 방향에 따라서 범위를 넘어가지 않거나, 벽을 만나지 않는 이상 계속 감시를 하며 보드값을 -1로 바꿔준다. 

2) dfs함수: 탐색깊이와 현재 보드 진행상태값을 넘겨주어 재귀적으로 호출한다. 탐색깊이가 설치된 씨씨티비의 개수와 같다면 최소값을 업데이트 해주고 반환한다. 재귀호출을 진행할때는 시계방향으로 회전이 가능한 모든 경우의수에 대해서 탐색을 해야하며, 탐색을 진행할때는 원래값이 담겨있는 배열을 깊은복사를 실시하여야한다. 깊은복사를 실시하지 않고 얕은복사를 하게되면 원배열이 계속 바뀌기 때문에 탐색이 제대로 진행되지 않는다. 

 

5) 구현코드   

import copy
n, m = map(int, input().split())
cctv = [] #cctv종류,x좌표,y좌표
board = [] #사무실 정보
#cctv 방향 정보
mode = [
    [],
    [[0], [1], [2], [3]],
    [[0, 2], [1, 3]],
    [[0, 1], [1, 2], [2, 3], [0, 3]],
    [[0, 1, 2], [0, 1, 3], [1, 2, 3], [0, 2, 3]],
    [[0, 1, 2, 3]],]

# 북 - 동 - 남 - 서
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]

for i in range(n):
    data = list(map(int, input().split()))
    board.append(data)
    for j in range(m):
        if data[j] in [1, 2, 3, 4, 5]:
            cctv.append([data[j], i, j])

def fill(board, mode, x, y): #감시
    for i in mode: #cctv 방향에 따라서 
        nx = x
        ny = y
        while True:
            nx += dx[i] 
            ny += dy[i]
            #범위를 넘어가면 중단
            if nx < 0 or ny < 0 or nx >= n or ny >= m:
                break
            #벽이면 중단 
            if board[nx][ny] == 6:
                break
            #감시가능 
            elif board[nx][ny] == 0:
                board[nx][ny] = -1

def dfs(depth, board): #탐색
    global min_value #최소값
    if depth == len(cctv): #탐색완료
        count = 0 
        for i in range(n): #사각지대 찾기
            count += board[i].count(0)
        #최소값 업데이트
        min_value = min(min_value, count)
        return
    
    temp = copy.deepcopy(board) #보드 복제 
    cctv_num, x, y = cctv[depth] #탐색할 cctv
    for i in mode[cctv_num]: #cctv의 방향에 따라서 
        fill(temp, i, x, y) #감시시작
        dfs(depth+1, temp) #재귀호출
        temp = copy.deepcopy(board) #보드 초기화


min_value = int(1e9)
dfs(0, board)
print(min_value)

 

⭐️난이도:  골드 4 ⭐️ 

https://www.acmicpc.net/problem/15683

 

15683번: 감시

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

www.acmicpc.net