https://www.acmicpc.net/problem/17141

 

17141번: 연구소 2

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

www.acmicpc.net

코드설명

DFS(깊이우선탐색) + 조합(Combination) + BFS(너비우선탐색) 를 활용하는 문제입니다.

 

코드로직입니다.

  1. 입력 받기:
    • 바이러스의 위치는 possibleVirus 리스트에 저장되며, 입력에서는 해당 위치를 2로 표시합니다.
    • 초기에 바이러스가 없어야 하는 위치(벽 등)는 virusHaveToMoveCnt를 통해 카운트하고, 나중에 바이러스가 퍼지는데 필요한 시간을 계산할 때 사용합니다.
  2. 조합 구하기:
    • getComb 함수를 통해 가능한 바이러스의 조합을 구합니다. 조합은 virusComb 배열에 저장됩니다.
  3. 바이러스 퍼뜨리기:
    • virusWork 함수에서는 선택된 바이러스들을 시작으로 BFS(너비 우선 탐색)를 수행하여 바이러스를 확산시킵니다.
    • visited 배열을 통해 방문한 위치를 기록하고, 바이러스가 있는 위치에서부터 상하좌우로 인접한 공간으로 바이러스를 확산시킵니다.
    • 각 단계별로 시간을 측정하고, 모든 바이러스가 퍼진 경우에만 최소 시간을 갱신합니다.
  4. 결과 출력:
    • 모든 바이러스를 퍼뜨리고 나면, 최소 시간인 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;
	}
}

 

 

+ Recent posts