이번에는 bottom-up방식으로 문제를 풀어보자.
bottom-up방식으로 문제를 풀 때는 아래서부터 어떠한 방식으로 dp배열을 채워나갈 것인가에 생각을 하게 된다. 1부터 차근차근 채워나가보자.
이 문제에서는 3가지 경우의 수를 갖고 있는데
1. x가 3으로 나누어 떨어지면 3으로 나눈다.
2. x가 2로 나누어 떨어지면 2로 나눈다.
3. 1을 뺀다.
이제부터 채워나갈 dp 배열은 값 N에 대하여 2 또는 3으로 나누어 떨어지면(N % 3 or 2 = 0;) 해당 값 중 가장 작은 값을 dp배열에 채워넣을 것이다. 1을 빼는 것은 모든 수에
적용한다.
dp[1] = 0;
dp[2] = 1;
dp[3] = 1;
dp[4] = 2;
dp[5] = 3;
dp[6] = 3;
dp[7] = 4;
dp[8] = 3;
dp[9] = 2;
dp[10] = 3;
따라서 위의 식대로 코드를 슈도코드를 짜보면 아래와같다.
dp[N] = N;
if(N % 2 == 0) {
dp[N] = Math.min(dp[N], dp[N / 2] + 1);
}
if (N % 3 == 0) {
dp[N] = Math.min(dp[N], dp[N / 3] + 1);
}
dp[N] = Math.min(dp[N], do[N - 1] + 1);
여기서 else if 를 쓰지않은 이유는 2로도 나누어떨어질 수 있고, 3으로도 나누어 떨어질 수 있기 때문이다.
맨 처음 dp[N] = N 으로 초기화한 이유는 이 후 조건에서 최솟값을 dp[N]에 대입헤야 하기 때문이다.
전체코드에는 top-down과 bottom-up 코드가 모두 있다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
static Integer DP[];
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
DP = new Integer[N + 1];
DP[0] = DP[1] = 0;
// System.out.println(recur(N));
top_down(N);
System.out.println(DP[N]);
}
private static int recur(int N) {
if (DP[N] == null) {
if (N % 6 == 0) {
DP[N] = Math.min(recur(N - 1), Math.min(recur(N / 3), recur(N / 2))) + 1;
} else if (N % 3 == 0) {
DP[N] = Math.min(recur(N / 3), recur(N - 1)) + 1;
} else if (N % 2 == 0) {
DP[N] = Math.min(recur(N / 2), recur(N - 1)) + 1;
} else {
DP[N] = recur(N - 1) + 1;
}
}
return DP[N];
}
private static void top_down(int N) {
for(int i = 2; i <= N; i++) {
DP[i] = i;
if(i % 2 == 0) {
DP[i] = Math.min(DP[i], DP[i / 2] + 1);
}
if(i % 3 == 0) {
DP[i] = Math.min(DP[i], DP[i / 3] + 1);
}
DP[i] = Math.min(DP[i], DP[i - 1] + 1);
}
}
}
이번문제에서는 몇 가지 조건이 존재했다.
1. 2로 나누기
2. 3으로 나누기
3. -1 연산하기
이 문제의 핵심은 N이 주어졌을 때 N에 대하여 조건들 중 부합하는 조건들끼리 비교하여 최솟값을 dp 배열에 넣는것이었다.
정리하자면, bottom-up기준 1부터 N까지 순회하며 조건에 부합하는 값들 중 최솟값을 dp에 넣는것이 주요포인트였다.