3197번: 백조의 호수
입력의 첫째 줄에는 R과 C가 주어진다. 단, 1 ≤ R, C ≤ 1500. 다음 R개의 줄에는 각각 길이 C의 문자열이 하나씩 주어진다. '.'은 물 공간, 'X'는 빙판 공간, 'L'은 백조가 있는 공간으로 나타낸다.
www.acmicpc.net
문제 읽기
처음에 문제를 읽었을 때는 일반적인 시뮬레이션 문제 같았다. 단순하게 생각하면 다음과 같은 구조로 풀이를 할 수 있을 것이다.
- 모든 판을 순회하며 물과 인접한 얼음에 없앤다는 표식을 남긴다
- 다시 모든 판을 순회하며 표식이 있는 얼음을 녹여 물로 만든다
- 위 과정이 끝났으면 백조 한 마리의 위치에서 BFS를 수행하여, 물로만 이동했을 때 다른 백조까지 도달하는 지 파악한다
- 두 백조가 만났다면 끝내고, 만나지 않았지만 1-3 과정을 반복한다.
하지만 플레 문제가 이렇게 단순할 리가 없다..!!!
의심은 했지만 한 번 고대로 풀어서 내봤더니 역시나 시간초과가 나왔다.
사실 시간 복잡도 한 번 계산하고 들어갔는데, 매우 중요한 걸 실수했다.
1 → RC4 = 150015004 = 9,000,000(9백만)
2 → RC = 15001500 = 2250000(약 2백만)
3 → RC = 15001500 = 2250000(약 2백만)
이렇게만 생각했는데 4번을 빼먹었다.
4 → 얼음이 다 녹는 시간. 최악의 경우 1500*루트2 ( = 한 변 1500인 정사각형 대각선 길이) = 3000
4번까지 해주면, (약 1300만) * 3000 = 39000000000(390억)
절대 안 됨@@
근데 도저히 방법은 떠오르지 않아서 질문 게시판 고전 글을 참고해봤다.
글 읽기 - 시간초과가 나는데 해결방법이 안떠오릅니다
댓글을 작성하려면 로그인해야 합니다.
www.acmicpc.net
Point 1
Point 2
풀이 방법이 여러가지인 거 같은데, 일단 이 게시판에 있는 풀이로 처음으로 해결해서, 이 내용을 포스팅 해보겠다.
문제 풀이
풀이에 필요한 알고리즘을 요약하자면 BFS + 이분탐색이다.
1. BFS - 얼음 녹이는 시간 구하기
- 입력 받을 때 모든 물의 좌표를 water라는 큐에 다 넣는다.
- bfs를 돌면서 인접한 얼음에 대해 depth+1을 해주면서 새로운 배열에 그 값을 써준다.
- 다음과 같은 전처리된 시간들을 얻을 수 있다.
- 이 시간들이 의미하는 바는, 해당 위치의 얼음이 녹는 시간이 된다. 이를 이용하여 백조가 언제 만날 수 있는지 확인할 수 있다.
💡 시간 복잡도 계산하기
내가 처음에 맘대로 풀었던 시뮬레이션 방식은, N이 작을 때만 가능하다. 이 문제의 경우에는 N이 너무 크고, 경우가 너무 많아지기 때문에 시뮬레이션이 거의 불가능한 것이다. (시간, 메모리 문제..)
그래서 이 풀이는시뮬레이션해서 얼음을 시간에 따라 녹이는 게 아니라, 얼음이 녹을 시간을 한 번의 BFS로 다 구해버리는 것이다. 이게 바로 전처리이다.
이 한 번의 전처리를 끝내놓으면, 위의 4번 반복문(시뮬레이션)을 돌 필요가 없어지고, 오직 두 마리의 백조가 만날 수 있는 지만 파악하면 끝이다.
2. 이분탐색 + BFS - 백조 만나기
- 이분탐색을 적용한다 (0~3000사이의 값)
- 가운데 시간인 mid 시간에 백조 하나의 위치부터 bfs를 돌린다
- 만났으면
- 이전 시간에 만났을 수도 있으니 (start, mid)를 다시 돌린다
- 안 만났으면
- 무조건 이후 시간에 만나는 것이니 (mid+1, end)를 다시 돌린다.
- 만났으면
- start ≥ end 되면 end를 리턴한다.
- 가운데 시간인 mid 시간에 백조 하나의 위치부터 bfs를 돌린다
💡 시간복잡도 계산하기
여기서 백조가 만나는 걸 확인하려면 BFS를 사용하는데, 위에서 구했듯이 BFS의 시간 복잡도는 약 200만, 얼음이 다 녹는데 최대 3000시간이 걸리므로 여기에 3000을 곱하면 6,000,000,000 = 60억이다. 따라서 시간 초과가 나기 때문에 이분탐색을 적용한 것이다.
이분 탐색을 적용하면 200만 * log2(3000)이므로 약 173,149 = 10만 번의 연산이 발생한다. 따라서 이분탐색 + BFS가 가능한 것이다.
코드
package ps.ㄱSolving;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Queue;
import java.util.StringTokenizer;
public class BOJ_P5_3197_백조의_호수_ver1_bfs_이분탐색 {
static class Pos{
int r, c;
int depth;
public Pos(int r, int c){
this.r = r;
this.c = c;
}
public Pos(int r, int c, int depth){
this.r = r;
this.c = c;
this.depth = depth;
}
}
static int R, C;
static char[][] map;
static int[][] days;
static Queue<Pos> water;
static int startR = -1, startC = -1;
static int endR = -1, endC = -1;
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(in.readLine());
R = Integer.parseInt(st.nextToken());
C = Integer.parseInt(st.nextToken());
map = new char[R][C];
days = new int[R][C];
water = new ArrayDeque<>();
for (int i = 0; i < R; i++){
String line = in.readLine();
int j = 0;
for (char c : line.toCharArray()){
if (c == 'L'){
if (startR == -1){
startR = i;
startC = j;
}
else{
endR = i;
endC = j;
}
water.offer(new Pos(i, j, 0));
}
else if (c == '.'){
water.offer(new Pos(i, j, 0));
}
map[i][j++] = c;
}
}
//logic
//1. 빙산이 언제 녹을 지 전처리 -> bfs
melting();
for (int i = 0; i < R; i++){
System.out.println(Arrays.toString(days[i]));
}
//2. 백조 bfs 이분탐색 수행
int day = binarySearch(0, 3000);
System.out.println(day);
}
private static int binarySearch(int start, int end) {
if (start >= end) return end;
int mid = (start + end) / 2;
if (bfs(mid)){ //만났다 - 왼쪽
end = mid;
}
else{ //못만났다 - 오른쪽 부분
start = mid + 1;
}
return binarySearch(start, end);
}
private static void melting() {
boolean[][] visited = new boolean[R][C];
//얼음이고, 방문하지 않은 곳이면 방문하면서 숫자를 체크
while(!water.isEmpty()){
Pos cur = water.poll();
for (int di = 0; di < 4; di++){
int nr = cur.r + dr[di];
int nc = cur.c + dc[di];
if (nr < 0 || nr >= R || nc < 0 || nc >= C || visited[nr][nc] || map[nr][nc] != 'X') continue;
visited[nr][nc] = true;
water.offer(new Pos(nr, nc, cur.depth+1));
days[nr][nc] = cur.depth+1;
}
}
}
static int[] dr = {-1, 1, 0, 0};
static int[] dc = {0, 0, -1, 1};
private static boolean bfs(int cutline) {
Queue<Pos> q = new ArrayDeque<>();
boolean[][] visited = new boolean[R][C];
q.offer(new Pos(startR, startC));
visited[startR][startC] = true;
while(!q.isEmpty()){
Pos cur = q.poll();
if (cur.r == endR && cur.c == endC) return true;
for (int di = 0; di < 4; di++){
int nr = cur.r + dr[di];
int nc = cur.c + dc[di];
if (nr < 0 || nr >= R || nc < 0 || nc >= C || visited[nr][nc] || days[nr][nc] > cutline) continue;
visited[nr][nc] = true;
q.offer(new Pos(nr, nc));
}
}
return false;
}
}
'Algorithm > Graph Search Algorithm' 카테고리의 다른 글
BOJ G4 1504 특정한 최단 경로 JAVA (0) | 2024.01.21 |
---|---|
BOJ G3 1238 파티 JAVA (0) | 2024.01.20 |
BOJ G5 1916 최소비용구하기 JAVA (0) | 2024.01.20 |
다익스트라, Dijkstra Algorithm (0) | 2024.01.20 |
BOJ G4 4179 불! JAVA (0) | 2023.07.13 |