코팅테스트/백준 문제 모음

백준 15683번 파이썬 문제풀이(감시)

유지광이 2022. 3. 6. 22:49
728x90

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

 

15683번: 감시

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

www.acmicpc.net

 

문제가 상당히 길어 URL로 남기겠습니다.


 

위 문제 굉장히 까다로운 구현이었습니다. 사실 어떻게 문제를 해결할지 머릿속에서 구현은 가능했으나 실제로 코드를 옮길려고 하니 생각해야 할 부분들이 너무나도 많았습니다. 특히 cctv가 닿을 수 있는 범위를 # 으로 처리하고 다시 #으로 되어있는 범위를 0으로 바꾸는 식으로 재귀로 구현할 생각했는데 이러한 로직은 문제가 있었습니다. 만약, 다른 cctv가 #으로 처리한 부분까지 0으로 바꿔버린다는 것입니다. 이러한 처리가 그다음 재귀를 돌때 영향을 끼쳤기에 다른방식으로 cctv의 범위 체킹을 해야했습니다. 쉽지 않았지만 재밌던 문제였습니다.

 

# 0을 감시되는 위치로 변경
def check(r, c, direct, depth):
    for dr, dc in direct:
        start_r = r  # 현재 위치를 저장
        start_c = c  # 현재 위치를 저장
        while True:  # 해당 방향으로 계속 탐색한다.

            nr = start_r + dr
            nc = start_c + dc

            if nr < 0 or nr >= N or nc >= M or nc < 0 or room[nr][nc] == 6:
                break

            if room[nr][nc] == 0:  # 만약 해당 위치가 0이라면
                room[nr][nc] = depth + 7  # 해당 카메라만의 범위임을 구분짓고 또한 그 값이 6이되면 안되니 +7로 해준다.

            start_r = nr  # 새로운 위치를 현재 위치로
            start_c = nc  # 새로운 위치를 현재 위치로


# 감시되는 위치를 다시 0으로 변경
def un_check(r, c, direct, depth):
    for dr, dc in direct:
        start_r = r
        start_c = c
        while True:

            nr = start_r + dr
            nc = start_c + dc

            if nr < 0 or nr >= N or nc >= M or nc < 0 or room[nr][nc] == 6:
                break

            if room[nr][nc] == depth + 7:  # 만약 depth+7 이라면
                room[nr][nc] = 0  # 다시 0 으로

            start_r = nr
            start_c = nc


# 재귀를 돈다.
def dfs(depth):
    global min_

    if depth == len(cctv):  # 만약 저장된 cctv 모두를 조사했다면
        count = 0  # 개수를 0으로 초기화
        for i in range(N):  # 방을 돌면서 사각지대의 개수를 센다.
            count += room[i].count(0)

        if count < min_:  # 만약 그 개수가 저장된 최솟값보다 작다면
            min_ = count  # 그 값을 최솟값으로 저장
        return  # 재귀 탈출

    if cctv[depth][2] == 1:  # 만약 cctv 1번이라면
        for i in [[(-1, 0)], [(0, 1)], [(1, 0)], [(0, -1)]]:  # 상, 우 , 하, 좌
            check(cctv[depth][0], cctv[depth][1], i, depth)  # cctv범위를 해당 depth + 7 로 바꿔준다.
            dfs(depth + 1)  # 바로 다음 재귀로 이동
            un_check(cctv[depth][0], cctv[depth][1], i, depth)  # 다시 체크한 범위를 0으로 바꿔준다.

    elif cctv[depth][2] == 2:
        for i in [[(-1, 0), (1, 0)], [(0, 1), (0, -1)]]:  # 상하 , 좌우
            check(cctv[depth][0], cctv[depth][1], i, depth)
            dfs(depth + 1)
            un_check(cctv[depth][0], cctv[depth][1], i, depth)

    elif cctv[depth][2] == 3:
        for i in [[(-1, 0), (0, 1)], [(0, 1), (1, 0)], [(1, 0), (0, -1)], [(-1, 0), (0, -1)]]:  # 상우, 우하, 하좌 , 좌상
            check(cctv[depth][0], cctv[depth][1], i, depth)
            dfs(depth + 1)
            un_check(cctv[depth][0], cctv[depth][1], i, depth)

    elif cctv[depth][2] == 4:
        for i in [[(-1, 0), (0, 1), (1, 0)], [(-1, 0), (1, 0), (0, -1)], [(-1, 0), (0, 1), (0, -1)],
                  # 상우하, 우하좌, 하좌상, 좌상우
                  [(0, 1), (1, 0), (0, -1)]]:
            check(cctv[depth][0], cctv[depth][1], i, depth)
            dfs(depth + 1)
            un_check(cctv[depth][0], cctv[depth][1], i, depth)

    else:
        for i in [[(-1, 0), (0, 1), (1, 0), (0, -1)]]:  # 상 우 하 좌
            check(cctv[depth][0], cctv[depth][1], i, depth)
            dfs(depth + 1)
            un_check(cctv[depth][0], cctv[depth][1], i, depth)


N, M = map(int, input().split())  # 세로 , 가로
room = []  # 방
cctv = []  # cctv위치 저장

for i in range(N):
    temp = list(map(int, input().split()))
    for j in range(len(temp)):
        if 1 <= temp[j] <= 5:  # 만약 1~5 사이라면
            cctv.append((i, j, temp[j]))  # i,j,cctv번호 를 저장
    room.append(temp)

min_ = 1 << 60 #최솟값을 저장할 변수
dfs(0) #재귀를 돈다.
print(min_)
728x90