새소식

Computer Science/코딩테스트 문제 풀이

[백준] [파이썬] [구현] [BFS] 14502번: 연구소

  • -

문제집 추천, 이 문제집에 나온 유형들만 공부해도 코딩테스트는 거뜬: https://www.acmicpc.net/workbook/by/BaaaaaaaaaaarkingDog

 

문제보러가기

 

14502번: 연구소

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크

www.acmicpc.net

 

 

시간 제한: 2초 

메모리 제한: 512MB

문제

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다.

연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다. 

일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다. 새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.

 

벽을 3개 세운 뒤, 바이러스가 퍼질 수 없는 곳을 안전 영역이라고 한다. 

즉, 벽을 3개 다 세우고 바이러스가 다 퍼진 이후, 0의 개수가 안전 영역의 크기이다. 

 

연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. (3 ≤ N, M ≤ 8)

둘째 줄부터 N개의 줄에 지도의 모양이 주어진다. 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 위치이다. 2의 개수는 2보다 크거나 같고, 10보다 작거나 같은 자연수이다.

빈 칸의 개수는 3개 이상이다.

출력

첫째 줄에 얻을 수 있는 안전 영역의 최대 크기를 출력한다.

 

예제 입력 1

7 7
2 0 0 0 1 1 0
0 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 0 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0

예제 출력 1

27

오랜만에 맞춘 문제 

구현문제로 글을 차근 차근 읽고 구현해야할 순서를 잘 잡으면 된다. 

 

1. 벽을 세운다. 

0인 구역에 벽 1을 3곳 세울 곳을 정해야 한다

 

2. 바이러스를 퍼트린다.

벽을 세우는 모든 경우의 수 마다 바이러스를 퍼트려야한다

 

3. 안전 영역, 즉, 0의 개수를 구해야한다. 

그리고 각 경우의 수별 0의 개수를 구해 그중 최대값을 출력하면된다.

 

코드를 다 짜고 나서 시간이 꽤 걸리는걸로 보였는데 다행이 통과하였다.


내가 푼 코드 

import sys
sys.stdin=open('input.txt', 'r')

from collections import deque
from itertools import combinations
import copy

moves = [(0,1), (1,0), (0,-1), (-1,0)]

def in_range(nx,ny):
    return 0<=nx<N and 0<=ny<M

if __name__ == '__main__':
    
    N,M=map(int, input().split())
    original_board = [list(map(int, input().split())) for _ in range(N)]
    check_board = copy.deepcopy(original_board)
    visited = [[0 for _ in range(M)] for _ in range(N)]
    idx = [(i,j) for i in range(N) for j in range(M)]
    Q=deque()
    
    res=0
    # 1. 0 인곳들 중에 3곳 뽑아 벽 세우기 
    for a,b,c in list(combinations(idx,3)):
        if all(original_board[e[0]][e[1]]==0 for e in [a,b,c]):  # 뽑은 3곳이 모두 빈곳 
            for e in [a,b,c]:
                check_board[e[0]][e[1]]=1     # 벽세우기 
            cnt=0
            # 2. 바이러스 퍼트리기 
            for i in range(N):
                for j in range(M):
                    if check_board[i][j]==2 and visited[i][j]==0:
                        Q.append((i,j))
                        visited[i][j]=1
                        while Q:
                            x,y=Q.popleft()
                            for dx,dy in moves:
                                nx=x+dx
                                ny=y+dy
                                if in_range(nx,ny) and check_board[nx][ny]==0 and visited[nx][ny]==0:
                                    visited[nx][ny]=1
                                    check_board[nx][ny]=2
                                    Q.append((nx,ny))
                                    
            # 3. 안전영역, 0의 개수 세기 
            for i in range(N):
                for j in range(M):
                    if check_board[i][j]==0:
                        cnt+=1
            
            res = max(res, cnt)
            check_board = copy.deepcopy(original_board)
            visited = [[0 for _ in range(M)] for _ in range(N)]
    
    print(res)

combinations를 안쓰고 백트래킹으로 하는 법 

import sys
sys.stdin=open('input.txt', 'r')

from collections import deque
from itertools import combinations
import copy

moves = [(0,1), (1,0), (0,-1), (-1,0)]

def in_range(nx,ny):
    return 0<=nx<N and 0<=ny<M

def BFS():
    global res
    Q=deque()
    check_board = copy.deepcopy(original_board)
    visited = [[0 for _ in range(M)] for _ in range(N)]
    cnt = 0
    for i in range(N):
        for j in range(M):
            if check_board[i][j]==2 and visited[i][j]==0:
                Q.append((i,j))
                visited[i][j]=1
                while Q:
                    x,y=Q.popleft()
                    for dx,dy in moves:
                        nx=x+dx
                        ny=y+dy
                        if in_range(nx,ny) and check_board[nx][ny]==0 and visited[nx][ny]==0:
                            visited[nx][ny]=1
                            check_board[nx][ny]=2
                            Q.append((nx,ny))
                                    
    # 3. 안전영역, 0의 개수 세기 
    for i in range(N):
        for j in range(M):
            if check_board[i][j]==0:
                cnt+=1
    
    res = max(res, cnt)

def makeWall(cnt):
    if cnt == 3:
        BFS()
        return 
    else:
        for i in range(N):
            for j in range(M):
                if original_board[i][j]==0:
                    original_board[i][j]=1
                    makeWall(cnt+1)
                    original_board[i][j]=0

if __name__ == '__main__':
    
    N,M=map(int, input().split())
    original_board = [list(map(int, input().split())) for _ in range(N)]
    visited = [[0 for _ in range(M)] for _ in range(N)]
    idx = [(i,j) for i in range(N) for j in range(M)]

    
    res=0
    makeWall(0)
    print(res)

 

 

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.