https://www.acmicpc.net/problem/12851
문제
수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때 걷는다면 1초 후에 X-1 또는 X+1로 이동하게 된다. 순간이동을 하는 경우에는 1초 후에 2*X의 위치로 이동하게 된다.
수빈이와 동생의 위치가 주어졌을 때, 수빈이가 동생을 찾을 수 있는 가장 빠른 시간이 몇 초 후인지 그리고, 가장 빠른 시간으로 찾는 방법이 몇 가지 인지 구하는 프로그램을 작성하시오.
입력
첫 번째 줄에 수빈이가 있는 위치 N과 동생이 있는 위치 K가 주어진다. N과 K는 정수이다.
출력
첫째 줄에 수빈이가 동생을 찾는 가장 빠른 시간을 출력한다.
둘째 줄에는 가장 빠른 시간으로 수빈이가 동생을 찾는 방법의 수를 출력한다.
풀이 방법
이전 숨바꼭질 1에서 조금 더 발전한...아니 뭔가 엄청 발전한 듯한 문제(물론 나는 바보여서 그렇고ㅠㅠ 알고나면 별거 아니다)
솔직히 처음에는 이거 뭐 쉽지라는 마음으로 엄청 단순하게 접근했고, 단순하게 풀었는데 당연히 이번에도 내 예상은 빗나갔고 어려움에 여러번 틀리고 해맸다.
결국 이 문제의 핵심은 '중복 좌표에 대한 방문을 허용' 하는 것이다.
즉 1->2 로 이동할때 1+1 도 2 라는 좌표로 이동할 수 있고, 1*2 도 2 라는 좌표로 이동할 수 있다는 것이다. 그렇기 때문에 이 모두를 계산해서 좌표-시간 배열(arr)에 넣어주어야 한다.
이때 중요한 점이 단순히 넣어주기만 하면 되는것이 아니라 2 라는 좌표에 도착하는 시간이 현재 좌표에 도착한 시간 +1 과 동일해야한다. 이는 2 -> 4 로 이동 할 때 2->3->4 로 이동하면 4 좌표까지 총 2초가 걸리지만 2 *2->4 로 이동하면 총 1초가 걸린다. 즉 굳이 더 많은 시간을 필요로하는 연산을 추가적으로 Queue 와 배열에 넣을 필요가 없고, 1->2 처럼 +1 이나 *2 가 똑같이 1 초가 걸리는 경우에만 Queue 와 배열에 넣어줘야 한다.
자세한 것은 코드의 추석 참고!
package baekJoon;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Quiz_12851 {
static int n, k;
static int[] arr = new int[100001];
static Queue<Integer> q = new LinkedList<Integer>();
static int min = Integer.MAX_VALUE;
static int cnt = 0;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
n = Integer.parseInt(st.nextToken());
k = Integer.parseInt(st.nextToken());
Arrays.fill(arr, 0);
if (n >= k) {
System.out.println(n - k);
System.out.println(1);
} else {
q.add(n);
arr[n] = 1;
findBrother2();
System.out.println(min);
System.out.println(cnt);
}
}
// +1 과 *2 는 서로 다르다!
static void findBrother2() {
while (!q.isEmpty()) {
int dis = q.poll();
if (min < arr[dis]) {
continue;
}
int[] move = {dis+1, dis-1, dis*2};
for(int i=0; i<3; i++){
int next = move[i];
if (next < 0 || next > 100000) {
continue;
}
if (next == k) {
min = arr[dis];
cnt++;
}
// next 는 현재 위치 dis 에서 +1, -1, *2 연산한 값
// 이 값이 0 이거나 ==> 한번도 해당 위치로 방문하지 않았거나
// 0 이 아니고, 해당 위치에 방문했을 때의 시간이 현재 dis 위치의 시간 +1 인 경우
// 즉 해당 위치에 이미 방문을 한 상태이며, next 에 도착했을 때 시간이 현재 dis에 도달한 시간 +1 인 경우
// - arr[next] = arr[dis]+1 - queue 에 next 값을 넣고 arr 배열에 next 의 시간을 arr[dis]+1 로 둔다
// 예를 들어 n = 1, k = 4 가 입력되었다고 생각해보자
// 이때 1 -> 4 로 가는 최소 횟수는 총 2가지 이다.
// 1->2(1+1) -> 3 -> 4
// 1->2(1*2) -> 3 -> 4
// 여기서 1*2 = 2 와 1+1 = 2 일 때 모두 현재 위치 1에서 단 한 번의 연산으로 2 라는 좌표에 도착할 수 있다
// 이때 1 -> 2 로 이동하는 경우 1+1 을 먼저해서 next = 2, arr[next] = arr[dis] +1 임으로 arr[next] = 2 가 된다
// 여기서 다시 한번 1->2 로 이동하는 1*2 를 생각해야한다. 1*2 도 1+1 과 마찬가지로 1번의 연산으로 2에 도착할 수 있다
// 이때는 arr[next] 가 0 이 아닌 상황 - arr[next] = 2 - 일 때
// arr[next] 의 값이 현재 위치인 1(dis) 의 값 +1 과 일치하는지, 즉 똑같이 next 라는 위치에 도달할때
// arr[next] 에 있는 시간이 현재 위치에서의 시간+1 - arr[dis] +1 - 인지 확인해야한다.
if (arr[next] == 0 || arr[next] == arr[dis] + 1) {
q.offer(next);
arr[next] = arr[dis] + 1;
}
}
}
}
}
- Reference
https://velog.io/@undefcat/%EB%B0%B1%EC%A4%80-12851-%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%88-2
'Java - 알고리즘' 카테고리의 다른 글
백준 - 14226 이모티콘 (0) | 2022.08.15 |
---|---|
백준 - 13549 숨바꼭질3 (0) | 2022.08.13 |
백준 1679 - 숨바꼭질 BFS (0) | 2022.08.09 |
백준 16953 - A - > B (0) | 2022.08.09 |
백준 1303 : 전투 DFS BFS 풀이 (0) | 2022.07.17 |
댓글