https://www.acmicpc.net/problem/17141
17141번: 연구소 2
인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 승원이는 연구소의 특정 위치에 바이러스 M개를 놓을 것이고, 승원이의 신호와 동시에 바이러
www.acmicpc.net
코드설명
DFS(깊이우선탐색) + 조합(Combination) + BFS(너비우선탐색) 를 활용하는 문제입니다.
코드로직입니다.
- 입력 받기:
- 바이러스의 위치는 possibleVirus 리스트에 저장되며, 입력에서는 해당 위치를 2로 표시합니다.
- 초기에 바이러스가 없어야 하는 위치(벽 등)는 virusHaveToMoveCnt를 통해 카운트하고, 나중에 바이러스가 퍼지는데 필요한 시간을 계산할 때 사용합니다.
- 조합 구하기:
- getComb 함수를 통해 가능한 바이러스의 조합을 구합니다. 조합은 virusComb 배열에 저장됩니다.
- 바이러스 퍼뜨리기:
- virusWork 함수에서는 선택된 바이러스들을 시작으로 BFS(너비 우선 탐색)를 수행하여 바이러스를 확산시킵니다.
- visited 배열을 통해 방문한 위치를 기록하고, 바이러스가 있는 위치에서부터 상하좌우로 인접한 공간으로 바이러스를 확산시킵니다.
- 각 단계별로 시간을 측정하고, 모든 바이러스가 퍼진 경우에만 최소 시간을 갱신합니다.
- 결과 출력:
- 모든 바이러스를 퍼뜨리고 나면, 최소 시간인 answer를 출력합니다. 만약 모든 바이러스를 퍼뜨릴 수 없는 경우에는 -1을 출력합니다.
모든 바이러스의 조합을 만들고, 각 조합에 대해 바이러스를 확산시키면서 최소 시간을 찾아내는 브루트 포스(완전 탐색) 를 사용하여 해결합니다.
조합을 구하는 방법은 여러가지 입니다.
1. M개만큼의 Size만큼만 구합니다. visited[M]으로 처리하는경우입니다. (즉, 3개의 바이러스 위치를 구한다고했을때 2 3 5 와 같이 바이러스 Index를 저장하는 방법입니다.)
2. 모든 바이러스의 개수 Size를 두고, 그 안에서 선택하느냐 안하느냐로 처리합니다.(즉, 5개의 바이러스에서 3개를 선택할경우 true true true false false 로 선택하는 경우입니다.)
속도와 메모리효율성은 1번이 높기에 1번으로 구현하는 것이 좋습니다.
가장 먼저 떠오르는 아이디어는 2번이 쉽게 떠오르기 쉽지만, M개의 바이러스를 선택한다는 개념으로 1번을 떠올리도록 하는 연습이 필요한 것 같습니다.
1번 방법을 떠올리기 어려운 이유중 하나는, 선택하지 않는 부분에 대한 정보를 가지고 있지 않기에 그런 것 같습니다.
그렇기에 바이러스의 위치를 처음에 모두 0 으로 만들어주고 시작하면, 가지고 있는 정보로만 처리할 수 있습니다.
추가로, 유의해야할점은
조합을 구할떄 자동으로 for문에서 i가 index를 기준으로 작동하므로, 굳이 visited를 처리할 필요가 없습니다.
for(int i=index; i<virusArr.size(); i++) {
// if(visited[i] == false) {
visited[i] = true;
combinationDFS(level + 1, i + 1, selected + 1);
visited[i] = false;
// }
}
아래는 5개의 Virus가 있는 상황에서 3개의 조합을 선택하는, 즉, 5C3 = 10 의 코드입니다.
입력
7 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 2 0 1 1
0 1 0 0 0 0 0
2 1 0 0 0 0 2
바이러스 조합
===============
true true true false false
===============
true true false true false
===============
true true false false true
===============
true false true true false
===============
true false true false true
===============
true false false true true
===============
false true true true false
===============
false true true false true
===============
false true false true true
===============
false false true true true
https://passionfruit200.tistory.com/845
[백준] 17142 연구소 3 - 조합 + 너비우선탐색(BFS) + 구현 JAVA
https://www.acmicpc.net/problem/17142 17142번: 연구소 3 인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 바이러스는 활성 상태와 비활성 상태가 있다. 가
passionfruit200.tistory.com
이전에 비슷한 문제인 연구소3은 위와 같이 possibleVirus 리스트를 활용하지않고, combVisited[][] 2차원 배열을 만들어서 사용할 바이러스의 위치를 true, false로 표시하여 진행했기에 바이러스의 경우마다 N^N의 맵을 모두 확인했어야했지만, 이 코드의 경우 해당 과정이 없어 더 속도가 빠릅니다.
또한 추가로 아래처럼 풀지않고, 직접 virusCnt와 virusHavetoMoveCnt가 같다면 Queue문을 종료시키며 time을 Return 시키는 방법도 좋아보입니다.
코드
조금 더 효율적인 코드입니다.
virus의 개수 M만큼만 선언하고, virus[level] 로 조합을 저장하여 사용하지 않는 바이러스들을 제외합니다.
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 {
public static int N, M;
public static int[][] arr;
public static ArrayList<Integer> possibleVirus = new ArrayList<Integer>();
public static int[] virusComb;
public static int virusIdx = 0, virusHaveToMoveCnt = 0;
public static int answer = 51 * 51;
public static boolean[][] visited;
public static int[][] storeArr;
public static int[] dx = {-1,1,0,0};
public static int[] dy = {0,0,-1,1};
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());
arr = new int[N][N];
virusComb = new int[M];
virusHaveToMoveCnt = N * N - M;
for(int i=0;i<N;i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++) {
arr[i][j] = Integer.parseInt(st.nextToken());
if(arr[i][j] == 2) {
possibleVirus.add( i * N + j);
arr[i][j] = 0;
}
if(arr[i][j] == 1) {
virusHaveToMoveCnt -= 1;
}
}
}
getComb(0, 0);
if(answer == 2601)
System.out.println(-1);
else
System.out.println(answer - 1);
}
public static void getComb(int level, int idx) {
if(level == M) {
virusWork();
return ;
}
for(int i=idx; i<possibleVirus.size();i++) {
virusComb[level] = possibleVirus.get(i);
getComb(level + 1, i + 1);
}
}
public static void virusWork() {
visited = new boolean[N][N];
Queue<Node> q = new LinkedList<Node>();
for(int i=0;i<M;i++) {
q.offer(new Node(virusComb[i] / N, virusComb[i] % N));
visited[virusComb[i] / N][virusComb[i] % N] = true;
}
int timeResult = 0;
int qSize = q.size();
int qCnt = 0;
int virusCnt = 0;
while(!q.isEmpty()) {
Node temp = q.poll();
qCnt += 1;
for(int dir = 0; dir<4; dir++) {
int nr = temp.r + dx[dir];
int nc = temp.c + dy[dir];
if(nr < 0 || nr >= N || nc < 0 || nc >= N) continue;
if(visited[nr][nc] == true) continue; //이미 방문했다면,
if(arr[nr][nc] == 1) continue; //만약 벽이라면 이동불가합니다.
virusCnt += 1;
visited[nr][nc] = true;
q.offer(new Node(nr, nc));
}
if(qCnt == qSize) {
timeResult += 1;
qSize = q.size();
qCnt = 0;
}
}
if(virusCnt == virusHaveToMoveCnt) { //모든 바이러스를 죽인경우 이므로 answer값과 갱신해봅니다.
answer = Math.min(answer, timeResult);
}
}
}
class Node{
int r;
int c;
public Node(int r, int c) {
this.r=r;
this.c=c;
}
}
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 {
public static int N,M, K;
public static int[][] map;
public static int answer = 50*50 + 1;
public static ArrayList<Node> virusArr = new ArrayList<Node>();
public static boolean[] visited;
public static int virusLocated = 99;
public static int emptySize = 0;
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];
emptySize = 0;
int virusCanSize = 0;
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] == 2) {
virusArr.add(new Node(i, j));
virusCanSize += 1;
}
if(map[i][j] == 0) {
emptySize += 1;
}
}
}
//전체 돌아야할 개수입니다.
emptySize += virusCanSize - M;
visited = new boolean[virusArr.size()];
combinationDFS(0, 0, 0);
if(answer == 50 * 50 + 1)
System.out.println("-1");
else
System.out.println(answer);
}
public static void combinationDFS(int level, int index, int selected) {
if(selected == M) {
int[][] storeMap = new int[N][N];
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
storeMap[i][j] = map[i][j];
}
}
System.out.println("===============");
for(int i=0;i<virusArr.size();i++) {
System.out.print(" "+visited[i]);
if(visited[i] == false) {
Node temp = virusArr.get(i);
map[temp.r][temp.c] = 0;
}
}
System.out.println();
BFS();
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
map[i][j] = storeMap[i][j];
}
}
return ;
}
if(index >= virusArr.size()) {
return ;
}
for(int i=index; i<virusArr.size(); i++) {
if(visited[i] == false) {
visited[i] = true;
combinationDFS(level + 1, i + 1, selected + 1);
visited[i] = false;
}
}
}
public static void BFS() {
int[] dx = {-1,1,0,0};
int[] dy = {0, 0, -1, 1};
Queue<Node> q = new LinkedList<>();
for(int i=0;i<virusArr.size();i++) {
if(visited[i] == true) {
Node temp = virusArr.get(i);
q.offer(new Node(temp.r, temp.c));
}
}
int qSize = q.size();
int qCnt = 0;
int time = 0;
int virusMoveCnt = 0;
while(!q.isEmpty()) {
Node temp = q.poll();
qCnt += 1;
for(int dir = 0; dir < 4; dir++) {
int nr = temp.r + dx[dir];
int nc = temp.c + dy[dir];
if(nr < 0 || nr >= N || nc < 0 || nc >= N) continue;
//벽이거나 이미 바이러스가 존재한다면 멈춰도 됩니다.
if(map[nr][nc] == 1 || map[nr][nc] == 2) continue;
virusMoveCnt += 1;
map[nr][nc] = 2;
q.offer(new Node(nr, nc));
}
if(qSize == qCnt) {
qSize = q.size();
qCnt = 0;
time += 1;
}
}
if(virusMoveCnt == emptySize) {
// System.out.println();
// System.out.println("TIME:"+time);
answer = Math.min(answer, time - 1);
}
}
}
class Node{
int r;
int c;
public Node(int r, int c) {
this.r = r;
this.c = c;
}
}
'알고리즘 > 백준' 카테고리의 다른 글
[백준] 18808 스티커 붙이기 - 구현(implementation) + 시뮬레이션(Simulation) JAVA (0) | 2023.11.30 |
---|---|
[백준] 17281 ⚾ - DFS(깊이우선탐색) + 일반 순열(Normal Permutation) + 구현 JAVA (0) | 2023.11.28 |
[백준] 1405 미친 로봇 - 완전탐색(BruteForce) + DFS(깊이우선탐색) + 확률론(Probability) JAVA (0) | 2023.11.27 |
[백준] 27172 수 나누기 게임 - 에라토스테네스의 체 JAVA (0) | 2023.11.27 |
[백준] 17090 미로 탈출하기 - DFS(깊이우선탐색) + DP(Dynamic Programming) JAVA (0) | 2023.11.27 |