th42500의 TIL

[Java] 깊이/너비 우선 탐색(DFS/BFS) - 게임 맵 최단거리 (Lv2) 본문

Algorithm/Programmers

[Java] 깊이/너비 우선 탐색(DFS/BFS) - 게임 맵 최단거리 (Lv2)

th42500 2022. 8. 4. 16:50

https://school.programmers.co.kr/learn/courses/30/lessons/1844

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

✔ 입출력 예시

게임 맵 최단거리 입출력 예시

 

 

💡 포인트

1️⃣ 최단거리를 구해야 하므로 BFS 알고리즘 활용

https://ichijeochi.tistory.com/43?category=1012068 

 

BFS (Breadth-First Search)

BFS (Breadth-First Search) 그래프 탐색 알고리즘 탐색(Search)이란 많은 양의 데이터 내에서 원하는 데이터를 찾는 과정을 의미 BFS 그래프에서 부모 노드를 먼저 방문한 후, 방문했던 부모 노드의 자식

ichijeochi.tistory.com

2️⃣ 현재 위치에서 갈 수 있는 곳을 탐색하기 위해 사방 탐색 진행 (배열 dr, dc 참고)
3️⃣ 도착하지 못하는 경우에는 -1 반환

 

 

❓ 풀이방법

1️⃣ BFS 함수에 필요한 변수들 넣기 (maps, 방문 배열 생성)

2️⃣ 해당 위치의 정보를 담을 class 생성(행 인덱스, 열 인덱스, 현재까지 지나온 칸의 수) & BFS 실행을 위한 Queue 생성

3️⃣ 처음 위치 정보 Queue에 담고 방문 배열에 방문 표시

4️⃣ Queue가 빌 때까지 반복문을 반복

5️⃣ Queue의 가장 첫 원소를 빼내어 해당 위치에서 전진할 수 있는 칸이 있는지 판별 후, 있다면 전진할 수 있는 칸의 정보

      Queue에 담기 👉 (새로운 칸의 행, 새로운 칸의 열, 이전 칸의 cnt + 1)

      이때, 전진할 수 있는 칸이 있는지 판별할 때는 반드시 새로운 행과 열의 인덱스들이 maps내에서 갈 수 있는 칸인지

      판별 후, 방문 여부 등을 판별해야 ArrayIndexOutOfBoundsException 발생 ❌

6️⃣ 반복문 진행 중 현재 뽑은 원소의 행과 열이 도착 지점과 같다면 정답변수에 현재 원소의 칸(cnt)의 값을 담기

7️⃣ 만약, 반복문이 종료되어도 도착지점에 도착하지 못했다면 -1 반환

 

 

✔ 소스코드

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

public class Solution { // 게임 맵 최단거리

	public static void main(String[] args) {
//		int[][] maps = {{1,0,1,1,1}, {1,0,1,0,1}, {1,0,1,1,1}, {1,1,1,0,1}, {0,0,0,0,1}};
		int[][] maps = {{1,0,1,1,1}, {1,0,1,0,1}, {1,0,1,1,1}, {1,1,1,0,0}, {0,0,0,0,1}};

		System.out.println(solution(maps));
	}

	private static int solution(int[][] maps) {
		// 1. 최단거리를 구하기 위해 BFS 활용
		return bfs(maps, new boolean[maps.length][maps[0].length]);
	}

	static class Spot {  // Q에 담을 원소의 정보
		int x;  // 행 인덱스
		int y;  // 열 인덱스
		int cnt;  // 현재 위치까지 지나온 칸의 수
		public Spot(int x, int y, int cnt) {
			this.x = x;
			this.y = y;
			this.cnt = cnt;
		}
	}
	
	private static int bfs(int[][] maps, boolean[][] v) {
		boolean flag = false;  // 도착하지 못하는 경우를 위한 flag
		int answer = 0;  // 정답변수
		int[] dr = {-1, 1, 0, 0};  // 행인덱스 이동배열(상 하 좌 우)
		int[] dc = {0, 0, -1, 1};  // 열인덱스 이동배열(상 하 좌 우)
		
		// 2. BFS를 실행하기 위한 Queue 생성
		Queue<Spot> q = new LinkedList<>();
		// 3. 처음 위치의 인덱스 q에 삽입
		q.add(new Spot(0, 0, 1));
		// 3. 처음 위치 방문한 것으로 표시
		v[0][0] = true;
		
		while(!q.isEmpty()) {  // 4. q의 원소가 모두 없어질 때까지 반복
			Spot s = q.poll();
			
			// 6. q가 비어있지 않아도 목표위치에 도착했다면 해당 위치까지 지나온 칸의 개수 정답변수에 넣고 반복문 멈춤
			if(s.x == maps.length-1 && s.y == maps[0].length-1) {
				answer = s.cnt;
				flag = true;  // 도착했으므로 flag 변수 변경
				break;
			}
			
			for(int i=0; i<dr.length; i++) {
				int nr = s.x + dr[i];
				int nc = s.y + dc[i];
				if(0 <= nr && nr < maps.length && 0 <= nc && nc < maps[0].length && !v[nr][nc] && maps[nr][nc] == 1) {  
                // 5. 전진할 수 있는 칸이 있다면 q에 담기
					q.add(new Spot(nr, nc, s.cnt+1));
					v[nr][nc] = true;  // 5. 방문한 것으로 처리
				}
			}
		}
		
		if(!flag) {  // 7. 방문하지 못했다면
			return -1;
		}
		
		return answer;
	}
}

 

 

❗ 결과

    👉 테스트 통과

SSAFY때, 알고리즘 스터디를 진행하며 최단거리문제들을 대부분 BFS로 해결했던 것이 기억나 바로 BFS로 문제를 풀었다.

코드 제출 후 효율성 테스트까지 있는 것을 보고 DFS로 풀었다면 시간초과가 떠 문제를 다시 풀어야 하지 않았을까 하는 생각이...

오랜만에 도전한 BFS는 전보다는 오래걸렸지만 한번에 테스트를 통과할 수 있었던 문제였다 😁👍

Comments