https://www.acmicpc.net/problem/1463
1463번: 1로 만들기
첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.
www.acmicpc.net
💭 문제
💬 풀이
Bottom-Up 방식으로 반복문을 이용해서 풀었습니다.
먼저 가능한 연산은 1) 3으로 나누기, 2) 2로 나누기, 3) 1을 빼기 3가지 경우로, 이 문제는 3가지 연산을 이용해서 N을 1로 만들 수 있는 최소한의 연산 횟수를 구하는 문제입니다.
여기서 주의해야 할 점은 단순히 큰 수로 연산하는 것이 최소 연산 횟수를 구할 수 있는 것은 아니라는 점입니다.
예를 들어 10의 경우, 연산 가능한 큰 수로 연산하면 10 -> 5(2로 나눔) -> 4(1을 뺌) -> 2(2로 나눔) -> 1(2로 나눔) 로 4번의 연산을 합니다. 하지만 실제 최소 연산 횟수는 10 -> 9(1을 뺌) -> 3(3으로 나눔) -> 1(3으로 나눔) 로 3번이 정답입니다.
일단 dp 테이블에는 i를 1로 만들기 위해 필요한 최소 연산 횟수가 들어갑니다. dp[1] = 0, dp[2] = 1, dp[3] = 1, ... 등이 될 것입니다.
i를 연산한다고 할 때, 가능한 경우는 i/3, i/2, i-1 가 있을 것입니다. 따라서 i를 1로 만들기 위해 필요한 최소 연산 횟수, 즉 dp[i]는 dp[i/3], dp[i/2], dp[i-1] 세 가지 중 최소인 경우에 현재 연산 횟수인 1번을 더한 값이 될 것입니다.
이를 점화식으로 나타내면 dp[i] = min(dp[i/3], dp[i/2], dp[i-1]) + 1 가 됩니다.
따라서 2부터 N까지의 dp 테이블을 차례대로 구하고 dp[N]을 출력하면 N을 1로 만들 수 있는 최소한의 연산 횟수를 구할 수 있을 것입니다.
단, 주의할 점은 dp[i/3]은 i가 3으로 나눠 떨어질 때, dp[i/2]는 i가 2로 나눠 떨어질 때만 가능한 연산이기 때문에 조건을 붙여줘야 합니다.
✔ 소스코드
- N은 최대 10⁶ 까지 나올 수 있기 때문에 dp 테이블의 크기는 1000001로 설정합니다. (0 ~ 1000000)
- 먼저, 1을 빼는 경우로 초기화를 한 뒤, 3으로 나누는 연산이 가능하고 그 경우가 더 작다면 dp[i/3] + 1로 바꿉니다. 만약 3으로 나누어 떨어지지 않는다면 다음 if 문으로 넘어갑니다.
- 다음, 2로 나누는 연산이 가능하고 그 경우가 더 작다면 dp[i/2] + 1로 바꿉니다. 이렇게 해서 3가지 연산 중 최적의 경우를 구할 수 있게 됩니다.
import java.io.*;
public class BJ1463 {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
// N <= 1000000
int[] dp = new int[1000001];
for(int i=2; i<=N; i++) {
// 1을 빼는 연산
dp[i] = dp[i-1] + 1;
// 3으로 나눠떨어진다면
if(i % 3 == 0) {
// 3으로 나눴을 때의 경우가 더 최적이라면 그것으로 교체
dp[i] = Math.min(dp[i], dp[i / 3] + 1);
}
// 2로 나눠떨어진다면
if(i % 2 == 0) {
// 2로 나눴을 때의 경우가 더 최적이라면 그것으로 교체
dp[i] = Math.min(dp[i], dp[i / 2] + 1);
}
}
System.out.println(dp[N]);
}
}
'Programming > Baekjoon' 카테고리의 다른 글
[백준] 1003번 - 피보나치 함수 (JAVA) (0) | 2023.06.03 |
---|---|
[백준] 9095번 - 1, 2, 3 더하기 (JAVA) (0) | 2023.06.03 |