[코딩테스트] 다이나믹 프로그래밍(1로 만들기)
reference : 2021 코딩테스트 기초(최백준) 강의를 공부하며 정리한 내용입니다.
1로 만들기(백준 1463번)
https://www.acmicpc.net/problem/1463
문제
- 정수 X에 사용할 수 있는 연산 세 가지
- X가 3으로 나누어 떨어지면 3으로 나눈다
- X가 2로 나누어 떨어지면 2로 나눈다
- 1을 뺀다
어떤 정수 N에 위와 같은 연산을 선택해서 1을 만들려고 한다. 연산을 사용하는 횟수의 최소값을 구하는 문제
풀이
- X -> X / 3 , X -> X / 2 , X -> X - 1
- 큰 문제가 작은 문제의 정답을 이용할 수 있다.
- X를 X/3 으로 만들고, X/3을 1로 만든다. 그 1로 만드는 횟수에 +1 하면 X를 X/3 하고 1로 만드는 횟수와 같아진다.
- X가 12라면 /3 = 4, /2 = 6, -1 = 11일때 4를 1로 만드는 법 2회, 6을 1로 만드는 법 2회, 11을 1로 만드는 법 4회 필요하다 했을 때 위처럼 작은 문제를 풀어 미리 구한 값을 이용해서 12를 1로 만드는 최소값을 구하면 3이 된다.
- 점화식 난이도가 낮은 경우는 문제에서 구해야 하는 값과 같은 경우가 많다.
- 점화식 : D[N] = N을 1로 만드는 최소 연산 횟수
- 다이나믹으로 풀 수 있는 건 어떤 큰 문제가 한 단계와 나머지 전체 단계로 나누어지는 경우가 많다.
- N을 N/3으로 만들고 N/3을 1로 만든다. N -> N/3은 1번이고 N/3 -> … -> 1 은 D[N/3]이다. 따라서 1+D[N/3] 이 된다.
- 2로 나누는 경우엔 1+D[N/2]
- 1빼는 경우엔 1+D[N-1]
- D[N] = min(D[N/3], D[N/2], D[N-1])+1
- 단 조건, D[N/3] 은 N%3==0 일 때, D[N/2]는 N%2==0일 때.
- 다이나믹에서 가장 중요한 것 중 하나는 가장 작은 크기의 문제를 찾는 것이다. 이 문제에선 1이다. 1->1 0번. 아무 연산도 사용하지 않으면 됨. D[1] = 0 (N≥2)
- 3을 나누는 것, 2로 나누는 것, 1을 빼는 우선순위로 N을 1로 만드는 건 반례 존재(10)
Top-down 방식
import java.util.*;
public class Main {
static int memo[];
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
memo = new int[n+1];
System.out.println(go(n));
}
private static int go(int n) {
if(n==1)
return 0;
if(memo[n]>0) //memo[n]에 값이 0보다 크다면 이미 실행했던 결과가 있단 뜻이니 return memo[n]
return memo[n];
memo[n] = go(n-1)+1; // 1빼기
if(n%3==0) { //3나누기
int temp = go(n/3)+1;
if(memo[n]>temp)
memo[n]=temp;
}
if(n%2==0) { //2나누기
int temp = go(n/2)+1;
if(memo[n]>temp)
memo[n]=temp;
}
return memo[n];
}
}
Bottom-up 방식
import java.util.*;
public class Main {
static int ans = 0;
static int memo[];
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
memo = new int[n+1];
memo[1]=0; //1일때 값을 먼저 정의해줘야 함
for(int i=2; i<=n; i++) {
memo[i] = memo[i-1]+1;
if(i%2==0 && memo[i]>memo[i/2]+1) {
memo[i] = memo[i/2]+1;
}
if(i%3==0 && memo[i]>memo[i/3]+1) {
memo[i] = memo[i/3]+1;
}
}
System.out.println(memo[n]);
}
}
댓글남기기