Repository: Algorithm-study-s10/Algorithm_Study
Pull request #66: w10_탈출 - 예린
Author: login: yxinot
Labels: name: BFS
Created: 2025-04-15T08:48:51Z | Merged: 2025-04-16T01:55:30Z

## #️⃣ 문제링크
<!-- url  첨부 -->

## 📝 풀이 내용
- map, water, gosumdochi 2차원 배열을 다 따로 만들고 값을 비교하는 방식으로 했는데, 접근 방식 자체는 나쁘지 않았다고 생각하는데 로직 구현에서 너무 헤매서 170줄이 넘는 어메이징 코드가 탄생해버렸습니다... 디버깅을 2시간 했는데 결국 반례 해결을 못해서 gpt 수정본도 같이 첨부합니다.

<!-- 접근 방식 및 코드 설명 (코드에 주석으로 자세히 달아놓아도 좋습니다!) -->

#### 제출 이력
![image](https://github.com/user-attachments/assets/935cd654-8b9b-4f86-911a-bdf9c35d2612)

<!-- 캡쳐한 이미지 -->


### 💬 리뷰 요구사항 (선택)
> 여기에 작성해주세요 :)
- ㅎ ㅏ................................ 너무힘들었어요..................................
<!--  리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요
 ex) 이 부분을 더 최적화하고 싶은데 좋은 방법이 있을까요? -->



Diff:
diff --git a/src/main/java/org/example/rt8632/w10_Escape/boj_3055.java b/src/main/java/org/example/rt8632/w10_Escape/boj_3055.java
new file mode 100644
index 0000000..fc0a933
--- /dev/null
+++ b/src/main/java/org/example/rt8632/w10_Escape/boj_3055.java
@@ -0,0 +1,178 @@
+package org.example.rt8632.w10_Escape;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.StringTokenizer;
+
+public class boj_3055 {
+	static char[][] map;
+	static int[][] water, gosumdochi;
+	static boolean[][] visited;
+	static int R, C;
+	static int[] dx = { 1, 0, -1, 0 };
+	static int[] dy = { 0, 1, 0, -1 };
+
+	public static void main(String[] args) throws Exception {
+		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+		StringTokenizer st = new StringTokenizer(br.readLine());
+		R = Integer.parseInt(st.nextToken());
+		C = Integer.parseInt(st.nextToken());
+
+		map = new char[R][C];
+		water = new int[R][C];
+		gosumdochi = new int[R][C];
+
+		int waterX = 0, waterY = 0;
+		int gosumdochiX = 0, gosumdochiY = 0;
+		int goalX = 0, goalY = 0;
+		boolean isWater = false;
+		for (int i = 0; i < R; i++) {
+			st = new StringTokenizer(br.readLine());
+			String s = st.nextToken();
+			for (int j = 0; j < C; j++) {
+				map[i][j] = s.charAt(j);
+				if (map[i][j] == 'S') {
+					gosumdochiX = i;
+					gosumdochiY = j;
+				}
+				if (map[i][j] == '*') {
+					isWater = true;
+					waterX = i;
+					waterY = j;
+				}
+				if (map[i][j] == 'D') {
+					goalX = i;
+					goalY = j;
+				}
+			}
+		}
+
+		waterBfs();
+		visited = new boolean[R][C];
+		gosumdochiBfs(gosumdochiX, gosumdochiY);
+
+		for (int i = 0; i < R; i++) {
+			for (int j = 0; j < C; j++) {
+				System.out.print(water[i][j] + " ");
+			}
+			System.out.println();
+		}
+
+		for (int i = 0; i < R; i++) {
+			for (int j = 0; j < C; j++) {
+				System.out.print(gosumdochi[i][j] + " ");
+			}
+			System.out.println();
+		}
+		boolean flag = false;
+		int ans = 100;
+		if (isWater) { // water 있을 때
+			for (int i = 0; i < 4; i++) {
+				int nx = goalX + dx[i];
+				int ny = goalY + dy[i];
+
+				if (nx < 0 || ny < 0 || nx >= R || ny >= C)
+					continue;
+				if(map[nx][ny] == '*' || map[nx][ny] == 'X')
+					continue;
+				if(gosumdochi[goalX][goalY] == 0 || gosumdochi[nx][ny] >= gosumdochi[goalX][goalY])
+					continue;
+				if (water[nx][ny] > gosumdochi[nx][ny]) {
+					flag = true;
+					if (ans > gosumdochi[nx][ny] + 1)
+						ans = gosumdochi[nx][ny] + 1;
+				}
+				else if(water[nx][ny] == 0 && gosumdochi[nx][ny] != 0) {
+					flag = true;
+					if (ans > gosumdochi[nx][ny] + 1)
+						ans = gosumdochi[nx][ny] + 1;
+				}
+				else if(gosumdochi[nx][ny] == 0 && map[nx][ny] == 'S') {
+					flag = true;
+					ans = 1;
+				}
+			}
+		} else { //water 없을 때
+			for (int i = 0; i < 4; i++) {
+				int nx = goalX + dx[i];
+				int ny = goalY + dy[i];
+
+				if (nx < 0 || ny < 0 || nx >= R || ny >= C)
+					continue;
+				if(gosumdochi[nx][ny] == 0 && map[nx][ny] != 'S')
+					continue;
+				if(gosumdochi[goalX][goalY] == 0 || gosumdochi[nx][ny] >= gosumdochi[goalX][goalY])
+					continue;
+				if(gosumdochi[nx][ny] == 0 && map[nx][ny] == 'S') {
+					flag = true;
+					if(ans >= gosumdochi[nx][ny] + 1)
+						ans = gosumdochi[nx][ny] + 1;
+				}
+				else if(gosumdochi[nx][ny] != 0) {
+					flag = true;
+					if(ans >= gosumdochi[nx][ny] + 1)
+						ans = gosumdochi[nx][ny] + 1;
+				}
+			}
+		}
+
+		if (flag)
+			System.out.println(ans);
+		else
+			System.out.println("KAKTUS");
+	}
+
+	static void waterBfs() {
+		Queue<int[]> q = new LinkedList<>();
+		for (int i = 0; i < R; i++) {
+	        for (int j = 0; j < C; j++) {
+	            if (map[i][j] == '*') {
+	                q.add(new int[]{i, j});
+	                visited[i][j] = true;
+	            }
+	        }
+	    }
+		while (!q.isEmpty()) {
+			int[] c = q.poll();
+			int cx = c[0];
+			int cy = c[1];
+			for (int i = 0; i < 4; i++) {
+				int nx = cx + dx[i];
+				int ny = cy + dy[i];
+
+				if (nx < 0 || ny < 0 || nx >= R || ny >= C)
+					continue;
+				if (visited[nx][ny] || map[nx][ny] == 'X' || map[nx][ny] == 'D')
+					continue;
+				visited[nx][ny] = true;
+				water[nx][ny] = water[cx][cy] + 1;
+				q.add(new int[] { nx, ny });
+			}
+		}
+	}
+
+	static void gosumdochiBfs(int x, int y) {
+		Queue<int[]> q = new LinkedList<>();
+		q.add(new int[] { x, y });
+		visited[x][y] = true;
+		while (!q.isEmpty()) {
+			int[] c = q.poll();
+			int cx = c[0];
+			int cy = c[1];
+			for (int i = 0; i < 4; i++) {
+				int nx = cx + dx[i];
+				int ny = cy + dy[i];
+
+				if (nx < 0 || ny < 0 || nx >= R || ny >= C)
+					continue;
+				if (visited[nx][ny] || map[nx][ny] == 'X' || map[nx][ny] == '*')
+					continue;
+				gosumdochi[nx][ny] = gosumdochi[cx][cy] + 1;
+				visited[nx][ny] = true;
+				q.add(new int[] { nx, ny });
+			}
+		}
+	}
+}
diff --git a/src/main/java/org/example/rt8632/w10_Escape/boj_3055_v2.java b/src/main/java/org/example/rt8632/w10_Escape/boj_3055_v2.java
new file mode 100644
index 0000000..e958e2a
--- /dev/null
+++ b/src/main/java/org/example/rt8632/w10_Escape/boj_3055_v2.java
@@ -0,0 +1,123 @@
+package org.example.rt8632.w10_Escape;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.StringTokenizer;
+
+public class boj_3055_v2 {
+    static char[][] map;
+    static int[][] water, gosumdochi;
+    static boolean[][] visited;
+    static int R, C;
+    static int[] dx = {1, 0, -1, 0};
+    static int[] dy = {0, 1, 0, -1};
+
+    public static void main(String[] args) throws Exception {
+        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+        StringTokenizer st = new StringTokenizer(br.readLine());
+        R = Integer.parseInt(st.nextToken());
+        C = Integer.parseInt(st.nextToken());
+
+        map = new char[R][C];
+        water = new int[R][C];
+        gosumdochi = new int[R][C];
+
+        int gosumdochiX = 0, gosumdochiY = 0;
+        int goalX = 0, goalY = 0;
+
+        for (int i = 0; i < R; i++) {
+            String s = br.readLine();
+            for (int j = 0; j < C; j++) {
+                map[i][j] = s.charAt(j);
+                if (map[i][j] == 'S') {
+                    gosumdochiX = i;
+                    gosumdochiY = j;
+                }
+                if (map[i][j] == 'D') {
+                    goalX = i;
+                    goalY = j;
+                }
+            }
+        }
+
+        // 물 BFS
+        waterBfs();
+
+        // 고슴도치 BFS
+        visited = new boolean[R][C];
+        gosumdochiBfs(gosumdochiX, gosumdochiY);
+
+        // 최종 도착 여부 판단
+        if (gosumdochi[goalX][goalY] == 0) {
+            System.out.println("KAKTUS");
+        } else {
+            System.out.println(gosumdochi[goalX][goalY]);
+        }
+    }
+
+    static void waterBfs() {
+        Queue<int[]> q = new LinkedList<>();
+        visited = new boolean[R][C];
+
+        for (int i = 0; i < R; i++) {
+            for (int j = 0; j < C; j++) {
+                if (map[i][j] == '*') {
+                    q.add(new int[]{i, j});
+                    visited[i][j] = true;
+                }
+            }
+        }
+
+        while (!q.isEmpty()) {
+            int[] c = q.poll();
+            int cx = c[0];
+            int cy = c[1];
+
+            for (int i = 0; i < 4; i++) {
+                int nx = cx + dx[i];
+                int ny = cy + dy[i];
+
+                if (nx < 0 || ny < 0 || nx >= R || ny >= C)
+                    continue;
+                if (visited[nx][ny] || map[nx][ny] == 'X' || map[nx][ny] == 'D')
+                    continue;
+
+                visited[nx][ny] = true;
+                water[nx][ny] = water[cx][cy] + 1;
+                q.add(new int[]{nx, ny});
+            }
+        }
+    }
+
+    static void gosumdochiBfs(int x, int y) {
+        Queue<int[]> q = new LinkedList<>();
+        q.add(new int[]{x, y});
+        visited[x][y] = true;
+
+        while (!q.isEmpty()) {
+            int[] c = q.poll();
+            int cx = c[0];
+            int cy = c[1];
+
+            for (int i = 0; i < 4; i++) {
+                int nx = cx + dx[i];
+                int ny = cy + dy[i];
+
+                if (nx < 0 || ny < 0 || nx >= R || ny >= C)
+                    continue;
+                if (visited[nx][ny] || map[nx][ny] == 'X')
+                    continue;
+                // 물보다 늦게 도착하면 이동 불가
+                if (water[nx][ny] != 0 && gosumdochi[cx][cy] + 1 >= water[nx][ny])
+                    continue;
+
+                gosumdochi[nx][ny] = gosumdochi[cx][cy] + 1;
+                visited[nx][ny] = true;
+                q.add(new int[]{nx, ny});
+            }
+        }
+    }
+}
+


Reviews:
user: login: seonmiki
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T09:58:21Z
user: login: seonmiki
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T09:59:03Z
user: login: YangHyeSeon
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T11:27:41Z

Review comments:
diff_hunk: @@ -0,0 +1,178 @@
+package org.example.rt8632.w10_Escape;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.StringTokenizer;
+
+public class boj_3055 {
+	static char[][] map;
+	static int[][] water, gosumdochi;
path: src/main/java/org/example/rt8632/w10_Escape/boj_3055.java
user: login: seonmiki
body: gosumdochi 변수명 귀요미,, 😊
created_at: 2025-04-15T09:58:20Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,178 @@
+package org.example.rt8632.w10_Escape;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.StringTokenizer;
+
+public class boj_3055 {
+	static char[][] map;
+	static int[][] water, gosumdochi;
+	static boolean[][] visited;
+	static int R, C;
+	static int[] dx = { 1, 0, -1, 0 };
+	static int[] dy = { 0, 1, 0, -1 };
+
+	public static void main(String[] args) throws Exception {
+		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+		StringTokenizer st = new StringTokenizer(br.readLine());
+		R = Integer.parseInt(st.nextToken());
+		C = Integer.parseInt(st.nextToken());
+
+		map = new char[R][C];
+		water = new int[R][C];
+		gosumdochi = new int[R][C];
+
+		int waterX = 0, waterY = 0;
+		int gosumdochiX = 0, gosumdochiY = 0;
+		int goalX = 0, goalY = 0;
+		boolean isWater = false;
+		for (int i = 0; i < R; i++) {
+			st = new StringTokenizer(br.readLine());
+			String s = st.nextToken();
+			for (int j = 0; j < C; j++) {
+				map[i][j] = s.charAt(j);
+				if (map[i][j] == 'S') {
+					gosumdochiX = i;
+					gosumdochiY = j;
+				}
+				if (map[i][j] == '*') {
+					isWater = true;
+					waterX = i;
+					waterY = j;
+				}
+				if (map[i][j] == 'D') {
+					goalX = i;
+					goalY = j;
path: src/main/java/org/example/rt8632/w10_Escape/boj_3055.java
user: login: seonmiki
body: 입력받으면서 동시에 위치 저장해주는 방법을 쓰셨군요! 저도 이렇게하니까 편하더라구요~
created_at: 2025-04-15T09:59:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,123 @@
+package org.example.rt8632.w10_Escape;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.StringTokenizer;
+
+public class boj_3055_v2 {
+    static char[][] map;
+    static int[][] water, gosumdochi;
+    static boolean[][] visited;
+    static int R, C;
+    static int[] dx = {1, 0, -1, 0};
+    static int[] dy = {0, 1, 0, -1};
+
+    public static void main(String[] args) throws Exception {
+        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+        StringTokenizer st = new StringTokenizer(br.readLine());
+        R = Integer.parseInt(st.nextToken());
+        C = Integer.parseInt(st.nextToken());
+
+        map = new char[R][C];
+        water = new int[R][C];
+        gosumdochi = new int[R][C];
+
+        int gosumdochiX = 0, gosumdochiY = 0;
+        int goalX = 0, goalY = 0;
+
+        for (int i = 0; i < R; i++) {
+            String s = br.readLine();
+            for (int j = 0; j < C; j++) {
+                map[i][j] = s.charAt(j);
+                if (map[i][j] == 'S') {
+                    gosumdochiX = i;
+                    gosumdochiY = j;
+                }
+                if (map[i][j] == 'D') {
+                    goalX = i;
+                    goalY = j;
+                }
+            }
+        }
+
+        // 물 BFS
+        waterBfs();
+
+        // 고슴도치 BFS
+        visited = new boolean[R][C];
+        gosumdochiBfs(gosumdochiX, gosumdochiY);
+
+        // 최종 도착 여부 판단
+        if (gosumdochi[goalX][goalY] == 0) {
+            System.out.println("KAKTUS");
+        } else {
+            System.out.println(gosumdochi[goalX][goalY]);
+        }
+    }
+
+    static void waterBfs() {
+        Queue<int[]> q = new LinkedList<>();
+        visited = new boolean[R][C];
+
+        for (int i = 0; i < R; i++) {
+            for (int j = 0; j < C; j++) {
+                if (map[i][j] == '*') {
+                    q.add(new int[]{i, j});
+                    visited[i][j] = true;
+                }
+            }
+        }
+
+        while (!q.isEmpty()) {
+            int[] c = q.poll();
+            int cx = c[0];
+            int cy = c[1];
+
+            for (int i = 0; i < 4; i++) {
+                int nx = cx + dx[i];
+                int ny = cy + dy[i];
+
+                if (nx < 0 || ny < 0 || nx >= R || ny >= C)
+                    continue;
+                if (visited[nx][ny] || map[nx][ny] == 'X' || map[nx][ny] == 'D')
+                    continue;
+
+                visited[nx][ny] = true;
+                water[nx][ny] = water[cx][cy] + 1;
+                q.add(new int[]{nx, ny});
+            }
+        }
+    }
+
+    static void gosumdochiBfs(int x, int y) {
+        Queue<int[]> q = new LinkedList<>();
+        q.add(new int[]{x, y});
+        visited[x][y] = true;
+
+        while (!q.isEmpty()) {
+            int[] c = q.poll();
+            int cx = c[0];
+            int cy = c[1];
+
+            for (int i = 0; i < 4; i++) {
+                int nx = cx + dx[i];
+                int ny = cy + dy[i];
+
+                if (nx < 0 || ny < 0 || nx >= R || ny >= C)
+                    continue;
+                if (visited[nx][ny] || map[nx][ny] == 'X')
+                    continue;
+                // 물보다 늦게 도착하면 이동 불가
+                if (water[nx][ny] != 0 && gosumdochi[cx][cy] + 1 >= water[nx][ny])
+                    continue;
+
+                gosumdochi[nx][ny] = gosumdochi[cx][cy] + 1;
path: src/main/java/org/example/rt8632/w10_Escape/boj_3055_v2.java
user: login: YangHyeSeon
body: 위에 water[nx][ny] = water[cx][cy] + 1; 부분도 그렇고 이 부분도 약간 dp 느낌이 나기도 하네요?
물과 도치가 해당 칸에 도착하기 위해 걸린 시간을 저장해두고 해당 칸에 물보다 도치가 늦게 도착하면 이동이 불가능한 것으로 판단하는 방법이 신선해요! 

created_at: 2025-04-15T11:27:41Z
author_association: COLLABORATOR

PR comments:
user: login: seonmiki
created_at: 2025-04-15T10:04:57Z
body: 엄청난 코드를 작성해버린 우리 예린이,,, 그래도 새로운 접근법으로 시도해본 점 매우 높이 평가합니다!!!! 🫳🫳
물확장과 고슴도치이동을 bfs로 모두 계산한 후에 비교하는 방식인데, 그럼 물확장한 시점이랑 이동시점을 어떻게 일치시키는 건가요?
author_association: COLLABORATOR
user: login: YangHyeSeon
created_at: 2025-04-15T11:37:14Z
body: 맵과 도치, 물의 좌표를 모두 따로 2차원 배열을 선언하고 이를 활용하는 방법을 사용한다면, 맵은 정적인 좌표이므로 값을 확인하는데만 사용하고 도치와 물의 좌표는 실시간으로 변화하는 모습을 반영하여 사용하는거네요!

만약 이렇게 구현한다면 1) 먼저 물을 이동시키고, 2) 도치의 이동에 있어서 bfs를 하면서 현재 도치의 위치에서 for문을 사용해 4방을 탐색하는 과정에서 if문의 조건으로 (맵 안에 있고 && 방문하지 않았고 && 맵의 지형과 비교했을 때 이동이 불가능하지 않고 && 물이 없는) 으로 해서 비교하면 될 것 같아요!
다만, 이 방법을 사용할 땐 물이 가지 못하는 곳(목표지점)이 있다는 점을 고려해서 물의 이동 시에도 맵의 지형을 비교하면서 확장해줘야 할 것 같아요.

어차피 인덱스로 배열의 값을 가져와서 탐색하기 떄문에 시간 복잡도 문제는 없읋 것 같고, R과 C의 크기도 최대 50으로 작아서 최악의 경우로 가정하더라도 공간 복잡도도 양호할 것 같아요! 
author_association: COLLABORATOR