코딩 테스트(Coding Test)/백준

[백준] 12852번 : 1로 만들기 2 - 자바(Java)

다문다뭉 2024. 12. 20. 10:53

Problem 🔒

문제

https://www.acmicpc.net/problem/12852

 

정수 X에 사용할 수 있는 연산은 다음과 같이 세 가지 이다.

  1. X가 3으로 나누어 떨어지면, 3으로 나눈다.
  2. X가 2로 나누어 떨어지면, 2로 나눈다.
  3. 1을 뺀다.

정수 N이 주어졌을 때, 위와 같은 연산 세 개를 적절히 사용해서 1을 만들려고 한다. 연산을 사용하는 횟수의 최솟값을 출력하시오.

입력

첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 자연수 N이 주어진다.

출력

첫째 줄에 연산을 하는 횟수의 최솟값을 출력한다.

둘째 줄에는 N을 1로 만드는 방법에 포함되어 있는 수를 공백으로 구분해서 순서대로 출력한다. 정답이 여러 가지인 경우에는 아무거나 출력한다.

더보기

예제 입력 1

2

예제 출력 1

1
2 1

예제 입력 2

10

예제 출력 2

3
10 9 3 1

Approach ⭕

  1. 배열 생성
    • dp[N+1][2]의 2차원 배열을 생성한다.
    • 입력받은 수 N까지 구하기 위해 N+1 크기로 생성한다.
    • dp[i][0] : i를 만들기 위한 이전 숫자를 저장한다.
    • dp[i][1] : i를 만들기 위한 최소 연산 횟수를 저장한다.
  2. 최소 연산 횟수
    • dp[i] = min(dp[i-1], dp[i/2], dp[i/3]) + 1
    • dp[i-1] : i의 최소 연산 횟수는 i-1의 연산 횟수 + 1 이다.
    • dp[i/2] : 만약, i/2==0이라면 dp[i-1]과 비교하여 최소값을 선택한다.
    • dp[i/3] : 만약, i/3==0이라면 이전 값과 비교하여 최소값을 선택한다.
  3. 1로 만드는 방법에 포함되어 있는 수
    • 더 작은 연산 횟수로 업데이트 할 때, 이전 숫자(i-1, i/2, i/3)로 저장한다.

Learned 😯

  • 각 숫자를 만들기 위한 최소 연산 횟수만 저장하는 것이 아니라, 거쳐야하는 이전 경로도 같이 저장해야한다는 것을 깨달았다.

Solution 💡

public class 만들기1 {
    static int N, dp[][];
    static StringBuilder sb = new StringBuilder();
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        dp = new int[N+1][2];
        for(int i=2; i<=N; i++){
            dp[i][1] = dp[i-1][1]+1;
            dp[i][0] = i-1;
            if(i%2==0 && dp[i][1]>dp[i/2][1]+1){
                dp[i][1] = dp[i/2][1]+1;
                dp[i][0] = i/2;
            }
            if(i%3==0 && dp[i][1]>dp[i/3][1]+1){
                dp[i][1] = dp[i/3][1]+1;
                dp[i][0] = i/3;
            }
        }
        sb.append(dp[N][1]+"\\n");
        while(N>0){
            sb. append(N+" ");
            N = dp[N][0];
        }
        System.out.println(sb);
    }
}