BFS 역시 DFS와 마찬가지로 코딩 테스트에 자주 나오는 유형이다.
BFS는 아래 그림과 같이 진행되는 탐색 알고리즘이다.
시작점으로부터 같은 이동거리에 있는 점들 순차적으로 탐색하는 방법이고, 동심원을 그리며 탐색을 하는 것으로 생각할 수도 있다.
시작점으로부터 이동거리 순으로 탐색을 하기 때문에 최단경로를 구하는데 유용하게 사용된다.
BFS는 자료구조 Queue를 사용해서 구현할 수 있다.
코딩 테스트를 위한 BFS는 미로와 같이 x, y 좌표를 이용하는 문제에 자주 사용되는 것 같다고 생각되어
미로 문제를 통해 BFS를 어떻게 코드로 표현하는지 알아보려고 한다.
bfs의 구현을 말로 설명하자면 다음과 같이 진행된다.
1. 탐색할 위치의 좌표를 queue에 넣고 방문처리 한다.
2. queue에서 좌표를 하나 꺼내고 꺼낸 좌표의 상, 하, 좌, 우를 탐색한다.
3. 상, 하, 좌, 우의 좌표 중에 벽이 아니고, 방문하지 않은 좌표만 queue에 넣고 방문처리 한다.
4. 2,3 의 과정을 queue가 빌때까지 반복한다.
만약 문제에 최소의 칸 수 라는 말이 없었다면 DFS로도 풀 수 있겠지만 최소의 칸 수라는 말 때문에 BFS를 이용하여 풀어야 하는 문제이다.
자세한 설명은 주석을 참고하면 좋을 것 같다.
https://www.acmicpc.net/problem/2178
문제
N×M크기의 배열로 표현되는 미로가 있다.
1 | 0 | 1 | 1 | 1 | 1 |
1 | 0 | 1 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 0 | 1 | 1 |
미로에서 1은 이동할 수 있는 칸을 나타내고, 0은 이동할 수 없는 칸을 나타낸다. 이러한 미로가 주어졌을 때, (1, 1)에서 출발하여 (N, M)의 위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 프로그램을 작성하시오. 한 칸에서 다른 칸으로 이동할 때, 서로 인접한 칸으로만 이동할 수 있다.
위의 예에서는 15칸을 지나야 (N, M)의 위치로 이동할 수 있다. 칸을 셀 때에는 시작 위치와 도착 위치도 포함한다.
입력
첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.
출력
첫째 줄에 지나야 하는 최소의 칸 수를 출력한다. 항상 도착 위치로 이동할 수 있는 경우만 입력으로 주어진다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
static int N, M; //가로, 세로 입력을 위한 변수
static int[][] arr; // N*M 미로 입력을 받기 위한 배열
static int[][] c; // 방문 여부를 표시하기 위한 배열
static int[] dx = { 0, -1, 0, 1 }; // 오른쪽 위쪽 왼쪽 아래쪽 확인을 위한 배열
static int[] dy = { 1, 0, -1, 0 };
static Queue<Integer> q1 = new LinkedList<Integer>(); // x좌표 bfs를 위한 queue
static Queue<Integer> q2 = new LinkedList<Integer>(); // y좌표 bfs를 위한 queue
// java에서 queue는 위와 같이 LinkedList를 이용하여 사용가능하다.
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine();
StringTokenizer st = new StringTokenizer(str, " ");
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
arr = new int[N + 1][M + 1];
c = new int[N + 1][M + 1];
for (int i = 0; N > i; ++i) {
str = br.readLine();
for (int j = 0; M > j; ++j) {
arr[i][j] = Integer.parseInt(str.substring(j, j + 1));
}
} // 입력
bfs(0, 0); // bfs 실행
}
static void bfs(int x, int y) {
q1.add(x); // 먼저 queue에 x,y좌표를 각각 넣고 시작
q2.add(y);
c[x][y] = 1; // x,y 좌표에 방문 표시
while (!q1.isEmpty()) { // queue가 빌때 까지 반복
x = q1.poll(); // queue에서 젤 먼저 넣은 x,y좌표를 꺼내어 현재 좌표로 이용
y = q2.poll();
if (x == N - 1 && y == M - 1) { // 현재 좌표가 미로의 출구라면
System.out.print(arr[x][y]); // 최소 칸 수 출력
break; // 후 반복문 종료
}
for (int i = 0; 4 > i; ++i) { // 현재좌표의 오른쪽 위쪽 왼쪽 아래쪽 확인
int nx = x + dx[i];
int ny = y + dy[i];
if (nx >= 0 && ny >= 0 && nx <= N - 1 && ny <= M - 1) {
// 바뀐좌표가 미로배열의 범위에 속하는지 확인
if (arr[nx][ny] == 1 && c[nx][ny] == 0) {
// 바뀐좌표가 벽이 아니고 , 방문하지 않았다면
c[nx][ny] = 1; // 방문표시
q1.add(nx); // queue에 바뀐좌표를 넣어줌
q2.add(ny);
arr[nx][ny] = arr[x][y] + 1; // 칸수 + 1
}
}
}
}
}
}
만약 입력이 다음과 같이 들어 왔다면
1 | 0 | 1 | 1 | 1 | 1 |
1 | 0 | 1 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 0 | 1 | 1 |
bfs(0,0) 이 끝난 후 arr 배열은 다음과 같이 바뀔 것이므로 arr[N-1][M-1]이 미로를 탈출할 때 사용되는 최소 칸 수를 나타냄.
1 | 0 | 9 | 10 | 11 | 12 |
2 | 0 | 8 | 0 | 12 | 0 |
3 | 0 | 7 | 0 | 13 | 14 |
4 | 5 | 6 | 0 | 14 | 15 |
추천 문제 :
1. https://www.acmicpc.net/problem/7576 토마토
풀이 : https://ckdgus.tistory.com/32
2. https://www.acmicpc.net/problem/7569 토마토
풀이 : https://ckdgus.tistory.com/33
3. https://www.acmicpc.net/problem/1697
'컴퓨터 > 알고리즘' 카테고리의 다른 글
[코딩테스트][알고리즘] 7. 투 포인터(Two Pointers) (0) | 2020.03.20 |
---|---|
[코딩테스트][알고리즘] 5. DFS (Depth First Search) 깊이 우선 탐색 (0) | 2020.03.03 |
[코딩테스트][알고리즘] 4. DP( Dynamic Programming) 다이나믹 프로그래밍 (0) | 2020.02.27 |
[코딩테스트][알고리즘] 3. Greedy(그리디) (0) | 2020.02.22 |
[코딩테스트][알고리즘] 2. Backtracking(빽트레킹) (0) | 2020.02.22 |