ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [DFS/BFS] 문제이름 : 음료수 얼려 먹기
    Algorithm/유형별 문제 풀기 2021. 7. 5. 11:59

    문제 설명

    N X M 크기의 얼음 틀이 있다. 구멍이 뚫린 부분은 0, 칸막이가 존재하는 부분은 1로 표시된다. 구멍이 뚫려 있는 부분끼리 상 하 좌 우로 붙어 있는 경우 서로 연결되어 있는 것으로 간주한다. 이때 얼음 틀의 모양이 주어졌을 때 생성되는 총 아이스크림의 개수를 구하는 프로그램을 작성하시오. 

     

    1. 사용 알고리즘 

    그래프 탐색 - BFS & DFS 

     

    2. 문제 해결 아이디어 

    주어진 좌표의 위치와 해당 위치의 상하좌우에 대해 구멍이 뚫려서 연결되어 있는지 탐색하면 된다. 이때 주어진 2차원 배열을 그래프 형태로 생각하면, DFS와 BFS 알고리즘을 적용해 해결할 수 있다.  

     

    3. 코드

    구현방식1) BFS(너비 우선 탐색)를 사용해 가까운 좌표부터 확인하기

    각 좌표가 상하좌우에 대해 움직일 수 있는 좌표의 범위를 dx[], dy[] 리스트로 미리 선언해둔다. 

    main 메서드 내에서 이중for문으로 주어진 좌표의 모든 위치에 대해 hole(구멍이 뚫린 부분)인 경우 BFS를 실행한다. 

    bfs 함수 내에서

    주어진 좌표의 row와 col을 각각 큐에 저장(enqueue) -> row와 col 값 꺼내기(dequeue) -> 주어진 좌표의 상하좌우에 대해        hole(구멍이 뚫린 경우)인 경우 큐에 저장(enqueue) -> 방문했음을 체크하기 위해(main에서 해당 좌표에 대해 bfs() 호출하지 않도록 하기 위해) visited로 설정

    import java.util.LinkedList;
    import java.util.Queue;
    
    import java.util.Scanner;
    
    public class Ex1 {
    	public static int mapRowSize, mapColSize;
    	
    	public static int dx[] = {-1, 1, 0, 0}; //행의 움직임 (상하좌우)
    	public static int dy[] = {0, 0, -1, 1}; //열의 움직임 (상하좌우) 
    	
    	public static int hole = 0, wall = 1, visited = 2;
    	
    	public static void bfs(int iceMap[][], int startVertexRow, int startVertexCol) { 
    		Queue<Integer> rowQueue = new LinkedList<Integer>();
    		Queue<Integer> colQueue = new LinkedList<Integer>();
    		
    		rowQueue.offer(startVertexRow); //enqueue
    		colQueue.offer(startVertexCol);
    		iceMap[startVertexRow][startVertexCol] = visited; 
    		
    		while(!rowQueue.isEmpty()) {
    			startVertexRow = rowQueue.poll(); //dequeue 
    			startVertexCol = colQueue.poll();
    			
    			for(int i = 0; i < dx.length; i++) {
    				int x = startVertexRow + dx[i]; //row -> 헷갈린 부분 : x가 좌표에서의 x값이 아니라 행임! 
    				int y = startVertexCol + dy[i]; //col -> y는 열임! 
    				
    				if(x != -1 && x != mapRowSize && y != -1 && y != mapColSize) { //x좌표 -> col, y좌표 -> row 
    					if(iceMap[x][y] == hole) {
    						rowQueue.offer(x);
    						colQueue.offer(y); 
    						iceMap[x][y] = visited; 
    					}
    				}
    			}
    		}
    	}
    	
    	public static void main(String[] args) {
    		Scanner kbd = new Scanner(System.in);
    		
    		mapRowSize = kbd.nextInt();
    		mapColSize = kbd.nextInt();
    		kbd.nextLine(); //버퍼 지우기 
    		
    		int iceMap[][] = new int[mapRowSize][mapColSize]; //그래프의 경우 -> 인접행렬이라고 생각하기 
    		
    		/* 2차원 리스트의 맵 정보 입력 받기 */
    		for(int i = 0; i < mapRowSize; i++) {
    			String str = kbd.nextLine(); //문자열로 입력받기 
    			for(int j = 0; j < mapColSize; j++) {
    				iceMap[i][j] = str.charAt(j) - '0'; //Char to Int 
    			}
    		}
    			
    		
    		int totalIceCream = 0; 
    		
    		for(int i = 0; i < mapRowSize; i++)
    			for(int j = 0; j < mapColSize; j++) {
    				if(iceMap[i][j] == hole) {
    					bfs(iceMap, i, j);
    					totalIceCream++; 
    				}
    			}
    		
    		System.out.println(totalIceCream);
    	}
    
    }

    구현방식2) DFS(깊이 우선 탐색)를 사용해 연결된 좌표에 대해 최대한 깊게 들어가 확인하기 

    main 메서드 내에서 이중for문으로 모든 좌표 위치에 대해 DFS를 수행한다. 

    dfs 함수 내에서 좌표 값이 hole(구멍이 뚫린 경우)인 경우 -> 해당 위치 방문 처리 -> 상하좌우에 대해 재귀함수 호출  

    import java.util.Scanner;
    
    public class Main {
    
    	public static int mapRowSize, mapColSize;
    	
    	public static int hole = 0, wall = 1, visited = 2;
    	
    	public static boolean dfs(int iceMap[][], int vertexRow, int vertexCol) {
    		//Map의 좌표 범위 체크 
    		if(vertexRow <= -1 || vertexRow >= mapRowSize || vertexCol <= -1 || vertexCol >= mapColSize)
    			return false;
    		
    		if(iceMap[vertexRow][vertexCol] == hole) {
    			//해당 노드 방문 처리
    			iceMap[vertexRow][vertexCol] = visited;
    			//상하좌우 좌표에 대해 재귀적 호출 
    			dfs(iceMap, vertexRow-1, vertexCol);
    			dfs(iceMap, vertexRow+1, vertexCol);
    			dfs(iceMap, vertexRow, vertexCol-1);
    			dfs(iceMap, vertexRow, vertexCol+1);
    			return true; 
    		}
    		
    		return false; 
    	}
    	
    	public static void main(String[] args) {
    		Scanner kbd = new Scanner(System.in);
    		
    		mapRowSize = kbd.nextInt();
    		mapColSize = kbd.nextInt();
    		kbd.nextLine(); //버퍼 지우기 
    		
    		int iceMap[][] = new int[mapRowSize][mapColSize]; //그래프의 경우 -> 인접행렬이라고 생각하기 
    		
    		/* 2차원 리스트의 맵 정보 입력 받기 */
    		for(int i = 0; i < mapRowSize; i++) {
    			String str = kbd.nextLine(); //문자열로 입력받기 
    			for(int j = 0; j < mapColSize; j++) {
    				iceMap[i][j] = str.charAt(j) - '0'; //Char to Int 
    			}
    		}
    			
    		int totalIceCream = 0; 
    		
    		for(int i = 0; i < mapRowSize; i++)
    			for(int j = 0; j < mapColSize; j++) {
    				//현재 위치에서 DFS 수행
    				if(dfs(iceMap, i, j))
    					 totalIceCream += 1;
    			}
    		
    		System.out.println(totalIceCream);
    
    	}
    }

     

    개선할 부분

    Q. Queue에 원소 값 2개를 저장할 수 없나? BFS 구현 시 row와 col 각각에 대해 2개의 큐 선언해서 저장했는데,  한 번에 저장할 방법을 찾아보자!

    방식1) Pair 역할을 담당할 새로운 클래스 생성하기

    주어진 좌표에 대해 Pair 클래스 객체 생성 -> 해당 객체를 Queue에 넣어주기 

    //나머지 부분은 위에서 작성한 코드와 동일함 
    //추가하고(Pair 클래스) 변경한(bfs함수) 코드 부분만 첨부함 
    
    class Pair{
    	public int row;
    	public int col;
    	
    	Pair(int row, int col){
    		this.row = row;
    		this.col = col;
    	}
    }
    
    public static void bfs(int iceMap[][], int startVertexRow, int startVertexCol) { 
    		Queue<Pair> queue = new LinkedList<Pair>();
    		
    		Pair pair = new Pair(startVertexRow, startVertexCol);
    		
    		queue.offer(pair);
    		iceMap[startVertexRow][startVertexCol] = visited; 
    		
    		while(!queue.isEmpty()) {
    			Pair dequeueVal = queue.poll();
    			startVertexRow = dequeueVal.row; 
    			startVertexCol = dequeueVal.col;
    			
    			for(int i = 0; i < dx.length; i++) {
    				int x = startVertexRow + dx[i]; //row -> 헷갈린 부분 : x가 좌표에서의 x값이 아니라 행임! 
    				int y = startVertexCol + dy[i]; //col -> y는 열임! 
    				
    				if(x != -1 && x != mapRowSize && y != -1 && y != mapColSize) { //x좌표 -> col, y좌표 -> row 
    					if(iceMap[x][y] == hole) {
    						pair = new Pair(x, y);
    						queue.offer(pair);
    						iceMap[x][y] = visited; 
    					}
    				}
    			}
    		}
    	}

    댓글

Designed by Tistory.