https://www.acmicpc.net/problem/14502
- 문제
인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다.
연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다.
일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다. 새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.
예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자.
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 |
이때, 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 곳이다. 아무런 벽을 세우지 않는다면, 바이러스는 모든 빈 칸으로 퍼져나갈 수 있다.
2행 1열, 1행 2열, 4행 6열에 벽을 세운다면 지도의 모양은 아래와 같아지게 된다.
2 1 0 0 1 1 0 1 0 1 0 1 2 0 0 1 1 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 |
바이러스가 퍼진 뒤의 모습은 아래와 같아진다.
2 1 0 0 1 1 2 1 0 1 0 1 2 2 0 1 1 0 1 2 2 0 1 0 0 0 1 2 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 |
벽을 3개 세운 뒤, 바이러스가 퍼질 수 없는 곳을 안전 영역이라고 한다. 위의 지도에서 안전 영역의 크기는 27이다.
연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오.
- 입력
첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. (3 ≤ N, M ≤ 8)
둘째 줄부터 N개의 줄에 지도의 모양이 주어진다. 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 위치이다. 2의 개수는 2보다 크거나 같고, 10보다 작거나 같은 자연수이다.
빈 칸의 개수는 3개 이상이다.
- 출력
첫째 줄에 얻을 수 있는 안전 영역의 최대 크기를 출력한다.
백준 14502 연구소 자바 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class b14502_laboratory {
static int N, M, max; //행, 열, 안전영역의 최대크기
static int[][] laboratoryMap; // 연구소크기
//좌, 우, 아래, 위 좌표
static int[] dy = { -1, 1, 0, 0 };
static int[] dx = { 0, 0, -1, 1 };
static ArrayList<virusPoint> arrVirus; //바이러스 위치
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
arrVirus = new ArrayList<virusPoint>();
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
laboratoryMap = new int[N][M];
max = 0;
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < M; j++) {
laboratoryMap[i][j] = Integer.parseInt(st.nextToken());
if (laboratoryMap[i][j] == 2) { //바이러스 위치값을 따로 기록합니다.
arrVirus.add(new virusPoint(i, j));
}
}
}
//벽 세우기를 시작합니다.
makeWall(0);
//안전영역 크기를 반환합니다.
System.out.println(max);
}
// 벽을 세웁니다.
public static void makeWall(int depth) {
//3개의 벽을 세웠으면 바이러스를 퍼트립니다.
if (depth == 3) {
spreadVirus();
return;
}
//3개의 벽을 세울때까지 반복합니다.
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (laboratoryMap[i][j] == 0) { //빈칸인 경우
laboratoryMap[i][j] = 1; //벽을 세웁니다.
makeWall(depth + 1); //3개의 벽을 세우기 위한 makeWall재귀를 실행합니다.
laboratoryMap[i][j] = 0; //다음 경우를 위해서 값을 초기화합니다.
}
}
}
}
//벽을 세우고 바이러스를 퍼트립니다.
private static void spreadVirus() {
// laboratoryMap -> copyMap으로 복사,
// 복사를 안하고 laboratoryMap를 그대로 사용한다면 다른 경우들이랑 중복되므로 바이러스를 퍼트릴때마다 복사해서 사용합니다.
int[][] copyMap = copyMap(laboratoryMap);
//바이러스 위치를 큐에 추가합니다.
Queue<virusPoint> que = new LinkedList<virusPoint>();
for (int i = 0; i < arrVirus.size(); i++) {
que.offer(new virusPoint(arrVirus.get(i).x, arrVirus.get(i).y));
}
// copyMap에 바이러스가 있다면 바이러스를 퍼트립니다.
while (!que.isEmpty()) {
int row = que.peek().x; // peek : queue의 첫번째 값 참조, 제거X
int col = que.poll().y; // poll : queue에 첫번째 값을 반환하고 제거, 비어있다면 null
for (int k = 0; k < 4; k++) {
int nextRow = row + dy[k];
int nextCol = col + dx[k];
// map의 범위에 존재하고 빈칸이라면 바이러스(2)를 대입합니다.
if (isValid(nextRow, nextCol) && copyMap[nextRow][nextCol] == 0) {
copyMap[nextRow][nextCol] = 2;
que.offer(new virusPoint(nextRow, nextCol)); //바이러스 위치를 큐에 추가합니다.
}
}
}
//해당경우를 max값에 집어넣습니다.
max = Math.max(max, countSafe(copyMap));
}
// map 복사
public static int[][] copyMap(int [][] arrVirus) {
int[][] copyMap = new int[N][M];
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
copyMap[i][j] = arrVirus[i][j];
}
}
return copyMap;
}
//바이러스 좌표
private static class virusPoint {
int x;
int y;
public virusPoint(int x, int y) {
this.x = x;
this.y = y;
}
}
// 입력된 연구소 크기의 유효성 검사
public static boolean isValid(int row, int col) {
if (row < 0 || row >= N || col < 0 || col >= M){
return false;
}
return true;
}
// 안전구역 크기(0) 세기
private static int countSafe(int[][] copyMap) {
int result = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (copyMap[i][j] == 0) {
result++;
}
}
}
return result;
}
}
너비 우선 탐색(Breadth-first search, BFS)과 Queue를 이용하여 풀이하는 문제입니다.
정리
제가 정리한 해당 코드의 흐름을 다음과 같습니다. 세부사항은 주석에 있습니다.
- 연구소의 크기 및 위치값을 입력받습니다. 여기서 바이러스 위치는 따로 입력을 합니다.
- 3개의 벽을 세웁니다.
- 너비우선탐색으로 바이러스를 퍼트립니다. 이때 아까 바이러스위치를 입력한 값으로 상, 하, 좌, 우로 바이러스를 퍼트립니다.
- 그리고 안전영역의 크기를 세어줍니다.
- 2~4번 과정을 반복하며 안전영역의 크기 최대값을 추출합니다.
감사합니다.
'JAVA > 알고리즘' 카테고리의 다른 글
[ 해시 Level2 ] 프로그래머스 위장 - 자바 JAVA (0) | 2021.07.18 |
---|---|
[ 완전탐색 Level2 ] 프로그래머스 카펫 - 자바 JAVA (0) | 2021.07.18 |
[ 그래프탐색 ] 백준 7576 토마토 - 자바 JAVA (0) | 2021.07.03 |
[ 그래프탐색 ] 2178 백준 미로탐색 - 자바 Java (0) | 2020.10.20 |
[ 깊이/너비 우선 탐색(DFS/BFS) Level3 ] 프로그래머스 네트워크 - 자바 Java (0) | 2020.08.18 |
댓글