PS/백준

15683번 - 감시

ForteQook 2022. 8. 30. 11:20

문제

 

각 감시카메라를 네 방향으로 돌려보며 전체 보드에서 빈 칸의 개수가 최소가 되게하는 경우를 찾는 전형적인 백트래킹, DFS 문제이다. 큰 구조는 미리 구해놓은 감시카메라 좌표를 백트래킹 하며, 네  방향씩 돌려보는 것이다. 카메라의 방향이 90도 돌아가는 것은 0,1,2,3을 각각 북,동,남,서라고 했을 때 1씩 더해주기만 하면 된다. 이때 감시카메라가 감시하는 영역은 서로 겹칠 수 있으므로, 백트래킹할 때 감시 영역을 원래대로 돌려놓는 작업에 주의를 기울일 필요가 있다.

# 북 동 남 서
dRow = [-1,0,1,0]
dCol = [0,1,0,-1]

n,m = map(int,input().split())
board = []
cam_loc = []
for row in range(n):
    li = []
    for col,v in enumerate(map(int,input().split())):
        if 1 <= v <= 5:
            cam_loc.append((row,col))
        li.append(v)
    board.append(li)
loc_len = len(cam_loc)


# 북 기준, 방향 인덱스
cams = [[],[0],[0,2],[0,1],[0,1,3],[0,1,2,3]]


def draw(d,_row,_col):
    _trace = []
    while 0 <= _row+dRow[d] < n and 0 <= _col+dCol[d] < m:
        _row += dRow[d]
        _col += dCol[d]
        if board[_row][_col] == 6:
            break
        elif board[_row][_col] == 0:
            _trace.append((_row, _col))
            board[_row][_col] = 9
    return _trace


def count_zeros():
    result = 0
    for row in range(n):
        for col in range(m):
            if board[row][col] == 0:
                result += 1
    return result


def dfs(idx):
    global answer
    if idx == loc_len:
        answer = min(answer,count_zeros())
        return
    row,col = cam_loc[idx]
    cam_num = board[row][col]
    for i in range(4):
        trace = []
        for d in map(lambda x : (x+i) % 4, cams[cam_num]):
            trace += draw(d,row,col)
        dfs(idx+1)
        for _row,_col in trace:
            board[_row][_col] = 0


answer = int(1e9)
dfs(0)
print(answer)

풀이 2

cctv의 최대개수가 8개밖에 안되므로, cctv의 좌표를 리스트에 넣어놓고 백트래킹으로 순회하며 모든 cctv의 감시영역을 펼쳤을 때 사각지대가 몇개 생기는지 확인하면 된다. 표시했던 감시영역을 다시 빈칸으로 되돌리기 위해 리스트에 표시했던 좌표를 넣어놓고 재귀 호출이 종료되면 빈칸으로 되돌리도록 한다.

# 북 서 남 동
dRow = [-1,0,1,0]
dCol = [0,-1,0,1]
cctv = [
    [],
    [0],
    [0,2],
    [0,1],
    [0,1,2],
    [0,1,2,3]
]

N,M = map(int,input().split())
# 0:빈칸, 1~5:카메라, 6:벽, 9:감시영역
board = [[6]*(M+2) for row in range(N+2)]
locs = []
dirs = []
for i in range(1,N+1):
    for j,v in enumerate(map(int,input().split())):
        board[i][j+1] = v
        if 1 <= v <= 5:
            locs.append((v, i, j+1))
            dirs.append([*cctv[v]])


def get_answer(idx):
    global answer
    if idx > len(locs)-1:
        result = sum(map(lambda x : x.count(0),board))
        answer = min(answer,result)
        return
    # idx 카메라 90도씩 회전
    for _ in range(4):
        i, x, y = locs[idx]
        cache = []
        # 감시영역 표시
        for dir in dirs[idx]:
            nx,ny = x+dRow[dir],y+dCol[dir]
            while board[nx][ny] != 6:
                if board[nx][ny] == 0:
                    cache.append((nx,ny))
                    board[nx][ny] = 9
                nx,ny = nx+dRow[dir],ny+dCol[dir]
        # 재귀호출
        get_answer(idx+1)
        # 감시영역 지우기
        for row,col in cache:
            board[row][col] = 0
        # 90도 회전
        dirs[idx] = list(map(lambda x : (x+1)%4,dirs[idx]))


answer = int(1e9)
get_answer(0)
print(answer)

'PS > 백준' 카테고리의 다른 글

15684번 - 사다리 조작  (0) 2022.09.01
16235번 - 나무 재테크  (0) 2022.09.01
14891번 - 톱니바퀴  (0) 2022.08.26
17143번 - 낚시왕  (0) 2022.08.26
16236번 - 아기 상어  (0) 2022.08.25