투로드
Coder ToLoad
투로드
전체 방문자
오늘
어제

블로그 메뉴

  • 홈
  • 알고리즘
  • CS
  • GITHUB
  • 태그
  • 분류 전체보기 (69)
    • Toy Project (0)
      • EternalSNS (0)
    • Algorithm (46)
      • BaekJoon (38)
      • Programmers (7)
      • Code Tree (1)
    • Computer Science (13)
      • JAVA (7)
      • DataBase (4)
    • Backend (7)
      • Spring (2)
      • JPA (2)
      • Django (3)
    • Mobile (2)
      • Android (2)
    • Unity (1)

인기 글

최근 글

hELLO · Designed By 정상우.
투로드

Coder ToLoad

Algorithm/BaekJoon

[JAVA] 백준 17141 : 연구소 2

2021. 12. 27. 17:34

문제

 

17141번: 연구소 2

인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 승원이는 연구소의 특정 위치에 바이러스 M개를 놓을 것이고, 승원이의 신호와 동시에 바이

www.acmicpc.net

 

해결방법

오랜만에 BFS 탐색 문제이다.

이 문제의 경우 연구소에 특정 위치에 바이러스를 놓아서 연구소 전체를 바이러스에 감염시키려는 승원이의 계획을 도와주는 문제이다.

 

문제에서 맵에 2로 표시된 구역이 바이러스를 놓을 수 있는 공간인데, 그 공간 중 M만큼 바이러스를 놓았을 때 연구소 전체를 감염시킬 수 있는 가장 빠른 시간을 구해야 한다.

즉, 모든 경우의 수를 탐색해야 하는 완전 탐색 문제라고도 할 수 있다.

 

조합을 찾을 때는 비트 마스킹을 사용해서 방문 처리를 해주었다.

또 조합을 찾았을 때 해당 조합에서 바이러스가 퍼지는 경우를 탐색할 때 새로 맵을 만들어야 하는데, 그럴 때마다 기존의 맵을 이중 for문을 사용해서 복제하는 것은 너무 비효율적이라고 생각해 벽 위치가 담겨있는 ArrayList를 만들어두고 새로운 맵을 만들 때 해당 벽 위치만 입력해주었다.

그리고 바이러스를 완전히 퍼뜨렸는지 여부를 체크하기 위해 빈 공간의 개수를 확인할 blankCnt를 선언해두고, 바이러스를 퍼뜨리면서 바이러스 개수를 카운트해서 두 개가 일치하면 모든 공간이 바이러스로 가득 찼다는 것을 의미해서 걸린 시간을 리턴하고 아니라면 Integer.MAX_VALUE를 통해 최댓값을 리턴해 해당 경우가 불가능함을 표시하였다.

 

코드를 구성한 순서는 다음과 같다.

  • 바이러스를 놓을 수 있는 곳 중 M만큼 놓는 경우를 찾는다.
  • 해당 경우를 탐색하기 위한 맵을 새로 만든다.
  • 새로 만든 맵에서 바이러스를 퍼뜨려보고 모든 공간에 바이러스가 퍼졌다면 걸린 시간을 리턴한다.
  • 모든 경우의 수를 찾아보고 가장 빨리 퍼뜨린 시간을 출력한다.

 

코드에 자세하게 주석을 달아놓았으니 참고해주길 바란다.

 

코드

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 Main_G4_17141 {
	static int[] dr = {-1, 1, 0, 0};
	static int[] dc = {0, 0, -1, 1};
	
	static int N, M, map[][], blankCnt, result;
	static ArrayList<Node> canVirus = new ArrayList<>(); // 바이러스를 놓을 수 있는 위치
	static ArrayList<Node> walls = new ArrayList<>(); // 벽의 위치
	
	static int[] combi; // 바이러스를 놓을 수 있는 조합

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		N = Integer.parseInt(st.nextToken());
		M = Integer.parseInt(st.nextToken());
		
		// 맵 입력받기
		map = new int[N][N];
		for (int i = 0; i < N; i++) {
			st = new StringTokenizer(br.readLine());
			for (int j = 0; j < N; j++) {
				map[i][j] = Integer.parseInt(st.nextToken());
				
				if (map[i][j] == 1) walls.add(new Node(i, j)); // 벽 위치 체크
				else blankCnt++; // 벽이 아니면 빈 공간이므로 빈 공간 개수 증가
				
				if (map[i][j] == 2) { // 바이러스를 놓을 수 있는 공간
					canVirus.add(new Node(i, j));
					map[i][j] = 0; // 바이러스를 놓을 수 있는 공간은 빈 공간인 0으로 바꿔줌
				}
			}
		}

		combi = new int[M];
		result = Integer.MAX_VALUE;
		combination(0, 0, 0); // 조합을 돌려 계산
		
		if (result == Integer.MAX_VALUE) System.out.println(-1);
		else System.out.println(result);
		
	}
	
	// 바이러스를 놓을 수 있는 조합 찾기
	static void combination(int start, int cnt, int flag) {
		// 탐색 완료
		if (cnt == M) {
			// 해당 조합에서 바이러스가 모두 퍼지는 시간 확인
			result = Math.min(result, goVirus());
			return;
		}

		// 비트마스킹을 이용한 조합 찾기
		for (int i = start; i < canVirus.size(); i++) {
			if ((flag & (1 << i)) == 0) {
				combi[cnt] = i;
				combination(i + 1, cnt + 1, flag | 1 << i);
			}
		}
	}
	
	// 바이러스 퍼뜨리기
	static int goVirus() {
		// bfs 탐색할 맵을 새로 구성
		int[][] useMap = new int[N][N];
		for (Node wall : walls) useMap[wall.r][wall.c] = 1; 
		
		// bfs 시작
		Queue<Node> q = new LinkedList<>();
		for (int idx : combi) {
			// 바이러스를 맵에 등록
			q.offer(canVirus.get(idx));
			useMap[canVirus.get(idx).r][canVirus.get(idx).c] = 2;
		}
		
		int virusCnt = 0; // 빈칸을 바이러스로 채운 개수
		int sec = -1; // 시간 체크
		
		while (!q.isEmpty()) {
			sec++; // 1초 증가
			
			// 현재 시간에 해당하는 바이러스들만 퍼뜨림
			int size = q.size(); 
			for (int i = 0; i < size; i++) {
				Node virus = q.poll();
				virusCnt++;
				
				for (int d = 0; d < 4; d++) {
					int nr = virus.r + dr[d];
					int nc = virus.c + dc[d];
					
					if (nr < 0 || nc < 0 || nr >= N || nc >= N) continue;
					if (useMap[nr][nc] == 0) {
						q.offer(new Node(nr, nc));
						useMap[nr][nc] = 2;
					}
				}
			}
		}
		
		// 빈 칸을 모두 바이러스로 채웠는지 확인
		if (blankCnt == virusCnt) return sec;
		else return Integer.MAX_VALUE;
	}
	
	static class Node {
		int r;
		int c;
		
		Node (int r, int c) {
			this.r = r;
			this.c = c;
		}
	}
}

 

저작자표시 (새창열림)

'Algorithm > BaekJoon' 카테고리의 다른 글

[JAVA] 백준 2042 : 구간 합 구하기  (0) 2023.04.13
[JAVA] 백준 12764 : 싸지방에 간 준하  (0) 2022.10.27
[JAVA] 백준 1766 : 문제집  (0) 2021.12.19
[JAVA] 백준 1162 : 도로포장  (0) 2021.12.16
[JAVA] 백준 1963 : 소수 경로  (0) 2021.11.30
    'Algorithm/BaekJoon' 카테고리의 다른 글
    • [JAVA] 백준 2042 : 구간 합 구하기
    • [JAVA] 백준 12764 : 싸지방에 간 준하
    • [JAVA] 백준 1766 : 문제집
    • [JAVA] 백준 1162 : 도로포장
    투로드
    투로드
    훌륭한 프로그래머가 되어가는 과정을 담아보는 중입니다.

    티스토리툴바