-
[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; } } } } }
'Algorithm > 유형별 문제 풀기' 카테고리의 다른 글
[정렬] 문제 이름 : 위에서 아래로 - List<E> 정렬, Collections (0) 2021.08.18 [DFS/BFS] 문제 이름 : 미로 탈출 (0) 2021.07.08 [구현] 문제이름 : 게임 개발 (0) 2021.06.30 [구현] 문제 이름 : 왕실의 나이트 (0) 2021.06.29 [구현] 문제 이름 : 시각 (0) 2021.06.29