알고리즘/이것이 코딩테스트다
16_연구소
happenundo
2024. 2. 1. 18:02
728x90
14502번: 연구소
인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크
www.acmicpc.net
안전 구역의 크기의 최댓값을 구하는 문제다.
벽은 3개 설치할 수 있는데, 벽은 안전구역에 설치할 수 있다.
전체 그래프 크기 N*M = 8*8 <= 64이므로 생각보다 작았다.
그리고 시간 제한이 2초였다.
아런 것들을 딱 보고, 이거 그냥 벽 3개 고를 수 있는 모든 경우의 수마다 DFS 돌면서 탐색한 다음에, 안전구역 개수 세서 최댓값 구하면 되는거 아닌가? 라는 생각이 들었다.
그리고 이 생각이 딱 맞았다.
내 풀이 코드
# 14502번: 연구소
from itertools import combinations
import copy, sys
sys.setrecursionlimit(10**6)
n, m = map(int, input().split()) # 가로, 세로 길이
virus = [] # 바이러스의 좌표를 저장하고 있는 리스트
empty = [] # 빈 칸의 좌표를 저장하고 있는 리스트(벽을 세울 수 있는 후보들을 저장하고 있는 리스트)
graph = []
for r in range(n):
row = list(map(int, input().split()))
graph.append(row)
for c in range(m):
if graph[r][c] == 2:
virus.append((r, c))
elif graph[r][c] == 0:
empty.append((r, c))
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
visited = [[False] * m for _ in range(n)] # 방문여부를 저장하는 리스트
def dfs(x, y, graph, visited):
visited[x][y] = True
graph[x][y] = 300 # 바이러스가 퍼져버린 칸은 300임
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
# 칸을 초과하는 경우
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
# 벽으로 간 경우
if graph[nx][ny] == 1:
continue
# 다른 바이러스의 칸으로 간 경우
# if graph[nx][ny] == 2:
# continue
if not visited[nx][ny]:
if graph[nx][ny] == 0 or graph[nx][ny] == 2:
dfs(nx, ny, graph, visited)
# 안전 영역의 개수를 리턴해주는 함수
def check_safe_place(graph):
n = len(graph)
m = len(graph[0])
answer = 0
for i in range(n):
for j in range(m):
if graph[i][j] == 0:
answer += 1
return answer
wall_candidate = list(combinations(empty, 3)) # 벽을 설치할 수 있는 좌표 3개씩을 저장한 리스트
temp_graph = copy.deepcopy(graph)
temp_visited = copy.deepcopy(visited)
answer = 0
for candidate in wall_candidate:
for w_x, w_y in candidate:
temp_graph[w_x][w_y] = 1
for v in virus:
x, y = v
dfs(x, y, temp_graph, temp_visited)
answer = max(answer, check_safe_place(temp_graph))
temp_graph = copy.deepcopy(graph)
temp_visited = copy.deepcopy(visited)
print(answer)
나는 DFS를 통해 풀었다.
그리고 벽이 될 수 있는 경우의 수를 itertools 라이브러리의 combination를 사용해서 풀었다.
다른 풀이를 보니 combination을 사용하지 않고, DFS/BFS를 통해 벽이 될 수 있는 경우의 수를 구할 수도 있다고 한다.
즉, 안전 영역을 계산할 때 외에도 벽이 될 수 있는 경우의 수를 구할 때도 DFS/BFS를 또 쓰는 것이다.
다른 풀이 코드
# 14502번: 연구소
n, m = map(int, input().split())
data = [] # 초기 맵 리스트
temp = [[0] * m for _ in range(n)] # 벽을 설치한 뒤의 맵 리스트
for _ in range(n):
data.append(list(map(int, input().split())))
# 4가지 이동 방향에 대한 리스트
dx = [-1, 0, 1, 0]
dy = [0, -1, 0, 1]
result = 0
# DFS를 이용해 각 바이러스가 사방으로 퍼지도록 하기
def virus(x, y):
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
# 상, 하, 좌, 우 중 바이러스가 퍼질 수 있는 경우
if nx >= 0 and nx < n and ny >= 0 and ny < m:
if temp[nx][ny] == 0:
# 해당 위치에 바이러스를 배치하고, 다시 재귀 수행
temp[nx][ny] = 2
virus(nx, ny)
# 현재 맵에서 안전 영역의 크기를 계산하는 메소드
def get_score():
score = 0
for i in range(n):
for j in range(m):
if temp[i][j] == 0:
score += 1
return score
# DFS를 이용해 울타리를 설치하면서, 매번 안전 영역의 크기 계산
def dfs(count):
global result
# 울타리가 3개 설치된 경우
if count == 3:
for i in range(n):
for j in range(m):
temp[i][j] = data[i][j]
# 각 바이러스의 위치에서 전파 진행
for i in range(n):
for j in range(m):
if temp[i][j] == 2:
virus(i, j)
# 안전 영역의 최댓값 계산
result = max(result, get_score())
return
# 빈 공간에 울타리 설치
for i in range(n):
for j in range(m):
if data[i][j] == 0:
data[i][j] = 1
count += 1
dfs(count)
data[i][j] = 0
count -= 1
dfs(0)
print(result)
위 방식은 백트래킹을 활용하는 방법이기도 하다.
아래 링크를 참고해서 이 문제에 사용된 백트래킹 기법에 대해 이해하자.
728x90