Post

BOJ_17613_점프 (Java)

BOJ_17613_점프 (Java)

[Platinum II] 점프 - 17613

문제 링크

성능 요약

메모리: 21952 KB, 시간: 212 ms

분류

분할 정복, 다이나믹 프로그래밍

제출 일자

2025년 2월 10일 12:07:56

문제 설명

개구리가 수직선 위의 0에서 출발해서 오른쪽(x좌표가 증가하는 방향)으로 점프들을 수행한 후 어떤 수 x > 0에 도착하려 한다. 이 때, 점프 간격은 1로부터 시작해서 항상 직전 점프한 간격의 2배로 증가해야 한다.

만일 점프간격을 2배씩 계속 증가시켜 마지막 점프에서 목표 수 x를 지나칠 것 같으면, 필요한 경우 언제든지 점프 간격을 다시 처음 상태인 간격 1로 되돌아 갈 수 있다. 이것을 재시작이라고 부른다. 예를 들어, 아래 <그림 1>과 같이 x = 19에 도달하기 위해서 2번의 재시작을 수행해서 (1+2+4+8)+(1+2)+(1)= 19 와 같이 7번의 점프로 도착할 수 있다.

<그림 1>

개구리가 0에서 출발해서 어떤 양의 정수 N에 도달하기 위한 점프 횟수의 최솟값을 J(N)으로 나타내고 N의 점프넘버라고 부를 것이다. 예를 들어, <그림 1>을 보면 J(1) = 1, J(3) = 2, J(7) = 3, J(15) = 4, J(16) = 5, J(18) = 6, J(19) = 7과 같음을 알 수 있다.

여러분은 어떤 특정 구간 [x, y]안의 수들의 점프넘버들 중 최댓값을 찾아서 출력한다. 즉, 아래 조건을 만족하는 w를 찾아서 출력한다.

w = max{J(i) | x ≤ i ≤ y}

입력

첫 번째 줄에는 여러분에게 주어질 구간의 개수 T가 주어진다.(1 ≤ T ≤ 2,000) 이후 T개의 줄에 대해 답을 구해야 할 구간을 나타내는 두 정수 x, y가 공백을 사이에 두고 주어진다 (1 ≤ x ≤ y ≤ 109).

출력

T개의 줄에 각각 하나의 정수를 출력한다. 각 줄에 출력되는 정수는 구간 [x, y]안의 수들의 점프넘버들 중 최댓값이다. 각 정수는 입력으로 주어지는 구간의 순서에 맞게 출력되어야 한다. 즉, 첫 번째 줄에 출력되는 정답은 첫 번째로 주어지는 구간에 대응되어야 한다.

문제 풀이

아이디어는 겨우 떠올렸는데 분기가 어려웠다.

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
 * Author: nowalex322, Kim HyeonJae
 */
import java.io.*;
import java.util.*;
public class Main {
    static BufferedReader br;
    static BufferedWriter bw;
    static StringTokenizer st;
    static StringBuilder sb = new StringBuilder();
    static int TC;
    static long X, Y;
    static long[] arr;
    public static void main(String[] args) throws Exception {
        new Main().solution();
    }
    public void solution() throws Exception {
        br = new BufferedReader(new InputStreamReader(System.in));
//        br = new BufferedReader(new InputStreamReader(new FileInputStream("src/main/java/BOJ_17613_점프/input.txt")));
        bw = new BufferedWriter(new OutputStreamWriter(System.out));
        TC = Integer.parseInt(br.readLine());
        // int 가 2^31-1까지니까 최대 31번점프가능
        arr = new long[31];
        for(int i=1; i<31; i++) {
            arr[i] = (1L << i) -1;
        }
        while(TC-->0){
            st = new StringTokenizer(br.readLine());
            X = Long.parseLong(st.nextToken());
            Y = Long.parseLong(st.nextToken());
            sb.append(maxJ(X, Y)).append('\n');
        }
        System.out.println(sb);
        bw.flush();
        bw.close();
        br.close();
    }
    // x까지 최소점프횟수
    private static int calJ(long x) {
        long tmp = 1;
        int j = 0;
        while (x>0) {
            x -= tmp;
            // 점프초기화
            if (2 * tmp > x) tmp = 1;
            else tmp *= 2;
            j++;
        }
        return j;
    }
    // [left ~ right] 최대점프횟수구하기
    private static int maxJ(long left, long right) {
        if(left == right) return calJ(right);
        if(left + 1 == right) return Math.max(calJ(left), calJ(right));
        // right보다 큰 2거듭제곱 J찾기
        int idx;
        for(idx=0; idx<31; idx++){
            if(arr[idx] > right) break;
        }
        if(idx==1) return 1;
        //J(i-1), J(i-2)
        long J_1 = arr[idx-1]; // J(i-1)
        long J_2 = arr[idx-2]; // J(i-2)
        // case1: 20~100 (left <= J2 인 경우)
        // J2까지 가는 경우와 J1까지 가는 경우 중 더 큰 값 선택
        if(left <= J_2) return Math.max((idx-2) + maxJ(0, J_2), (idx-1) + maxJ(0, right-J_1));
        // case4: 64~100 (left > J1 인 경우)
        // 예: [64~100] 구간처럼 시작점이 J1보다 큰 경우
        // 전체 구간을 J1만큼 빼서 재귀적으로 처리
        if(left > J_1) return (idx-1) + maxJ(left-J_1, right-J_1);
        // case3: 40~100 (J2 < left < J1 인 경우)
        // 예: [32~62] 구간처럼 시작점이 J2와 J1 사이인 경우
        // J2를 밟고 가는 경우와 J1까지 가는 경우 중 더 큰 값 선택
        if(left < J_1) return Math.max((idx-2) + maxJ(left-J_2, J_2), (idx-1) + maxJ(0, right-J_1));
        // case2: (left == J1 인 경우)
        // 예: [63~100] 구간처럼 시작점이 정확히 J1인 경우
        // J1까지는 idx-1번의 점프로 도달, 나머지는 재귀로 처리
        return (idx-1) + maxJ(0, right-J_1);
    }
}
This post is licensed under CC BY 4.0 by the author.