Problem
수빈이는 동생이랑 숨바꼭질을 하고 있습니다.
수빈이는 N 에서 시작하고 동생은 K 에 있습니다.
수빈이는 1초에 +1, -1, *2 위치로 순간이동이 가능합니다.
수빈이가 동생을 찾는 가장 빠른 시간과 가장 빠른 시간으로 찾는 방법이 몇 가지 인지 구하는 문제입니다.
Solution
백준 1697번 숨바꼭질 문제의 진화형입니다.
최단 시간을 구하라는 점은 똑같지만, 이번에는 최단 시간에 만나는 모든 경우를 구해야 합니다.
그래서 푸는 방법이 조금 다릅니다.
1. BFS 를 종료 기준을 시간으로 탐색
BFS에는 시간이 0 부터 차례대로 쌓입니다.
만약 수빈이가 동생을 2 초에 만났고, BFS 탐색이 3 초를 넘어가면 이미 최단 시간이 아니기 때문에 그 이후는 더 볼 필요가 없습니다.
2. 중복 방문의 허용
수빈이가 최단 시간에 동생을 만나는 방법은 여러 가지가 있을 수 있습니다.
예를 들어 입력으로 1 4
가 주어졌다고 합니다.
그러면 수빈이가 동생을 만나는 최단 루트는 *2, *2 로 1 2 4
입니다.
하지만 +1, *2 로 움직이면 똑같이 1 2 4
루트로 동생을 만날 수 있습니다.
만약 위치 2에서의 중복 방문을 허용하지 않는다면 확인 할 수 없습니다.
3. 그러나 모든 중복을 허용하지 않기
똑같은 위치에 다시 방문했을 때 세가지 경우를 생각해봐야 합니다.
3.1. 이전 방문 시간과 현재 방문 시간이 일치할 때
time[next] == time[now] + 1
인 경우입니다.
동일한 시간에 도착했다면 최종 목적지에 도달하는 시간도 동일할 가능성이 있기 때문에 Queue 에 넣어줍니다.
3.2. 이전 방문 시간이 현재 방문 시간보다 빠른 경우
time[next] < time[now] + 1
인 경우입니다.
현재 방문 시간이 더 느리기 때문에 목적지에 최단 시간으로 도달할 가능성이 없습니다.
제외해줍니다.
3.3. 이전 방문 시간이 현재 방문 시간보다 늦은 경우
time[next] > time[now] + 1
인 경우입니다.
현재 시간이 더 빠르기 때문에 Queue 에 넣어줍니다.
그러나 사실 발생하지 않는 케이스입니다.
BFS 는 시간 순서로 진행되고 있습니다.
1 초에 도달하는 곳을 모두 확인하고 2 초에 도달할 수 있는 곳을 모두 확인하고.. 3초에..
따라서 이전에 방문한 곳이 현재 시간보다 클 가능성은 없습니다.
실제 로직에 영향을 주진 않으므로 Queue 에 넣는 조건으로 사용해도 괜찮습니다.
방금 예시로 든 1 2 4
를 다시 생각해봅니다.
두 경우 다 1초에 2로 이동하기 때문에 중복 방문을 해도 괜찮습니다.
하지만 만약 2에 방문하는 시간이 2초, 3초로 다를 경우, 더 늦은 3초는 볼 필요가 없기 때문에 방문을 허용하지 않습니다.
Java Code
import java.util.*;
import java.io.*;
class Main {
static int N, K;
static int[] time = new int[100001];
static int minTime = 987654321;
static int count = 0;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
if (N >= K) {
System.out.println((N-K) + "\n1");
return;
}
bfs();
System.out.println(minTime + "\n" + count);
}
static void bfs() {
Queue<Integer> q = new LinkedList<Integer>();
q.add(N);
time[N] = 1;
while (!q.isEmpty()) {
int now = q.poll();
// now 방문 시간이 최소 시간보다 크면 뒤는 더 볼 필요 없음
if (minTime < time[now]) return;
for (int i=0; i<3; i++) {
int next;
if (i == 0) next = now + 1;
else if (i == 1) next = now - 1;
else next = now * 2;
if (next < 0 || next > 100000) continue;
if (next == K) {
minTime = time[now];
count++;
}
// 첫 방문이거나 (time[next] == 0)
// 이미 방문한 곳이어도 같은 시간에 방문했다면 (time[next] == time[now] + 1)
// 경우의 수에 추가될 수 있기 때문에 Queue 에 한번 더 넣어줌
if (time[next] == 0 || time[next] == time[now] + 1) {
q.add(next);
time[next] = time[now] + 1;
}
}
}
}
}
'알고리즘 문제 > 백준' 카테고리의 다른 글
백준 14954번. Happy Number (Java) (0) | 2020.04.03 |
---|---|
백준 13913번. 숨바꼭질 4 (Java) (1) | 2020.03.30 |
백준 2293 동전 1 (Java) (0) | 2020.03.28 |
백준 2437번. 저울 (Java) (2) | 2020.03.17 |
백준 1912번. 연속합 (Java) (0) | 2019.10.09 |