개발/코딩테스트

[Baekjoon] 1052. 물병 #G5 #Java

brobro332 2025. 3. 16. 14:17
반응형
문제
지민이는 N개의 물병을 가지고 있다. 각 물병에는 물을 무한대로 부을 수 있다. 처음에 모든 물병에는 물이 1리터씩 들어있다. 지민이는 이 물병을 또 다른 장소로 옮기려고 한다. 지민이는 한 번에 K개의 물병을 옮길 수 있다. 
하지만, 지민이는 물을 낭비하기는 싫고, 이동을 한 번보다 많이 하기는 싫다. 따라서, 지민이는 물병의 물을 적절히 재분배해서, K개를 넘지 않는 비어있지 않은 물병을 만들려고 한다.

물은 다음과 같이 재분배 한다. 먼저 같은 양의 물이 들어있는 물병 두 개를 고른다. 그 다음에 한 개의 물병에 다른 한 쪽에 있는 물을 모두 붓는다. 이 방법을 필요한 만큼 계속 한다.
이런 제약 때문에, N개로 K개를 넘지않는 비어있지 않은 물병을 만드는 것이 불가능할 수도 있다. 다행히도, 새로운 물병을 살 수 있다. 상점에서 사는 물병은 물이 1리터 들어있다.

예를 들어, N=3이고, K=1일 때를 보면, 물병 3개로 1개를 만드는 것이 불가능하다. 한 병을 또다른 병에 부으면, 2리터가 들어있는 물병 하나와, 1리터가 들어있는 물병 하나가 남는다. 만약 상점에서 한 개의 물병을 산다면, 2리터가 들어있는 물병 두 개를 만들 수 있고, 마지막으로 4리터가 들어있는 물병 한 개를 만들 수 있다.
예제 입력 1
3 1


예제 출력 1

1

예제 입력 2

13 2


예제 출력 2

3

예제 입력 3

1000000 5


예제 출력 3

15808
제약조건
첫째 줄에 N과 K가 주어진다. N은 10^7보다 작거나 같은 자연수이고, K는 1,000보다 작거나 같은 자연수이다.

첫째 줄에 상점에서 사야하는 물병의 최솟값을 출력한다. 만약 정답이 없을 경우에는 -1을 출력한다.

 
문제풀이

public class Main {
    public static void main(String[] args) throws IOException {
        // 1. 변수 선언 및 초기화
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int k = Integer.parseInt(st.nextToken());
        int count = 0, result = 0, amount = 1;

        // 2. 물병 크기 구하기
        while (n > 0) {
            if ((n & 1) == 1) {
                pq.add(amount);
                count++;
            }
            amount *= 2;
            n /= 2;
        }

        // 3. 조건을 만족할 때까지 가장 작은 물병 두 개를 합침
        while (count > k) {
            if (pq.size() >= 2) {
                // 가장 작은 두 개 물병 꺼내기
                int first = pq.poll();
                int second = pq.poll();

                // 꺼낸 두 물병이 같으면 카운트 다운
                if (first == second) {
                    pq.add(first + second);
                    count--;
                    continue;
                }

                // 구매해야 하는 물병 수 누적
                result += (second - first);

                // 채운 물병을 다시 적재
                pq.add(second);
                pq.add(second);
            }
        }

        // 4. 출력
        System.out.println(result);
    }
}
  • 우선순위 큐를 통해 그리디스럽게 풀었다.
  • 우선순위 큐가 내부적으로 어떻게 동작하는지는 다음 블로그에서 확인하였다.

[Java] PriorityQueue(우선순위 큐) 클래스 사용법 & 예제 총정리

우선순위 큐(Priority Queue)란? 일반적으로 큐는 데이터를 일시적으로 쌓아두기 위한 자료구조로 스택과는 다르게 FIFO(First In First Out)의 구조 즉 먼저 들어온 데이터가 먼저 나가는 구조를 가집니다

coding-factory.tistory.com

  • 나머지 연산을 비트 연산자를 통해 효율적으로 개선하였다.
    • if (n & 1 == 1)과 if (n % 2 == 1) 조건 모두 같은 결과를 기대할 수 있다.
    • 그런데 비트 연산자를 통한 방법이 CPU 명령어 수준에서 최적화되므로 더 빠르다고 한다.
    • 반복문에서 반복 횟수가 늘어날 수록 빛을 발할 것이다.
코드가 조금 불필요하게 길어진 느낌이 있기는 하지만.. 이번에는 선택과 집중을 위해 눈물을 머금고 넘어가려고 한다. 🤪