여러 칸으로 나뉘어져 있는 직사각형의 지도 (가로와 세로의 크기는 각각 50 이하)
각 칸은 육지(L)나 바다(W)로 표시되어 있음
이 지도에서 상하좌우로 이웃한 육지로만 이동 가능하며, 한 칸 이동하는데 한 시간 소요됨
보물은 서로 간에 최단 거리로 이동하는 데 있어 가장 긴 시간이 걸리는 육지 두 곳에 나뉘어 묻혀있음
육지를 나타내는 두 곳 사이를 최단 거리로 이동하려면 같은 곳을 두 번 이상 지나가거나, 멀리 돌아가면 안 됨
보물 지도가 주어질 때, 보물이 뭍혀 있는 두 곳 간의 최단 거리로 이동하는 시간을 구하여라
// [input]
5 7
WLLWWWL
LLLWLLL
LWLWLWW
LWLWLLL
WLLWLWW
// [output]
8
⇒ bfs로 육지의 두 정점사이를 최단 거리로 이동하는 데 그 값들 중 가장 큰 값을 찾는 문제
[전역 변수]
int N, M // 맵의 가로 & 세로 크기
char [][] map // 2차원 배열 지도
int maxDist=0 // 두 정점 사이의 최댓값 저장
int[] dr // 4방향 탐색: 행의 값 변화
int[] dc // 4방향 탐색: 열의 값 변화
[main]
main()
1. [input]
- N, M
- map[N][M]
2. [logic]
for( i=0 ~ N)
for (j=0 ~ M)
map[i][j] == 'L'
findMaxDist(i,j)
3. [output]
sysout(maxDist)
[f: findMaxDist]
static void findMaxDist(int i, int j)
1. visited[N][M] 초기화: false
visited[i][j] = t
2. 큐 생성
q <- (i,j,0) // 현재 행, 열, 거리 누적값 넣기
3. while(!q.isEmpty())
currR, currC, currD = q.poll()
maxDist 갱신 = maxDist(currD, maxDist)
4방향 탐색
newR, newC
1) isValid 체크 -> 아니면 continue
2) visited 체크 -> true면 continue
3) map[newR][newC] != 'L'이면 continue
4) visited[newR][newC] = true
5) 큐에 (newR, newC, currD+1) 추가
public class Main {
static int N,M;
static char[][] map;
static int maxDist = 0;
static int[] dr= {-1,1,0,0};
static int[] dc= {0,0,-1,1};
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// [input]
String[] tmp = br.readLine().split(" ");
N = Integer.parseInt(tmp[0]); // 세로 크기 = 행의 개수
M = Integer.parseInt(tmp[1]); // 열
map = new char[N][M];
for(int i=0; i<N; i++) {
String temp = br.readLine();
for(int j=0; j<M; j++) {
map[i][j]=temp.charAt(j);
}
}
//System.out.println(Arrays.deepToString(map));
// [logic]
// 모든 육지 쌍 중 가장 긴 최단거리
for(int i=0; i<N; i++) {
for(int j=0; j<M;j++) {
if (map[i][j]=='L') { // !! 모든 L에 대해
findMaxDist(i,j);
}
}
}
// [output]
System.out.println(maxDist);
}// main
// 하나의 클러스터당 돌릴 bfs
private static void findMaxDist(int cr, int cc) {
boolean[][] visited = new boolean[N][M];
visited[cr][cc]=true;
Deque<int[]> q = new ArrayDeque<>();
q.add(new int[] {cr, cc,0}); // 현재 행, 열, 거리 누적값
while(!q.isEmpty()) {
int[] currRCD = q.poll();
int currR = currRCD[0];
int currC = currRCD[1];
int currD = currRCD[2];
maxDist = Math.max(currD, maxDist);
for(int d=0; d<4; d++) {
int newR = currR + dr[d];
int newC = currC + dc[d];
if(newR<0|| newR >=N || newC<0|| newC>=M) continue;
if(!visited[newR][newC]&& map[newR][newC]=='L') {
visited[newR][newC] = true;
q.add(new int[] {newR,newC,currD+1}); //!! 한 레벨씩 거리 증가시키기
}
}
}
}// f: findMaxDist
}
for (int i=0; i<N; i++) {
for (int j=0; j<M; j++) {
if (map[i][j] == 'L') {
findMaxDist(i, j); // BFS 실행
}
}
}
모든 칸을 돌면서 값이 L일 경우마다 BFS 수행
❌ boolean[][] visited 배열을 main 함수에서 선언해서
방문하지 않은 L만 bfs 하는 방식 → 섬이 여러 개로 나눠지 경우 한 번만 탐색하도록 함
...
static boolean[][] visited;
public static void main(String[] args) {
...
// visited 배열 초기화
visited = [N][M];
...
// X 방문하지 않은 L에 대해서만 X
for(int i=0; i<N; i++){
for(int j=0; j<M; ++) {
if(map[i][j]=='L' && !visited[i][j]){
findMaxDist(i,j);
}
}
}
✅ 모든 좌표의 L을 시작점으로 BFS를 돌려야 하는 이유
각 L이 속한 클러스터 안에서 그 지점이 클러스터의 끝단이 아닐 수도 있기 때문에
모든 L을 시작점으로 BFS를 한 번씩 수행하면, 모든 가능한 경로를 다 탐색하게 되어 전역적으로 최대 거리를 보장할 수 있음 (완전 탐색 방식)
L L L L L (0,0)에서 BFS → 최장 거리 = 4 (0,2)에서 BFS → 최장 거리 = 2 (0,4)에서 BFS → 최장 거리 = 4 ⇒ 중간에서 시작하면 실제 최대 지름을 얻지 못함이 코드에서 잘못된 부분이나 개선할 점이 있다면 언제든지 댓글로 알려주세요.
알고리즘을 작성하면서 더 좋은 코드를 만들 수 있도록 도와주시면 정말 감사하겠습니다! 🙂