정수 X에 사용할 수 있는 연산은 다음과 같이 세 가지 이다.
정수 N이 주어졌을 때, 위와 같은 연산 세 개를 적절히 사용해서 1을 만들려고 한다. 연산을 사용하는 횟수의 최솟값을 출력하시오.
첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.
첫째 줄에 연산을 하는 횟수의 최솟값을 출력한다.
#include <iostream>
#include <cstring> //memset
using namespace std;
/* 조건 */
#define MAX_N 1000001
/* 변수 */
int N;
int memo[MAX_N];
/* 함수 */
int dp(int num, int cnt) {
if(num == 1) return memo[1] < cnt? memo[1] + 1 : cnt + 1;
int& ret = memo[num];
if(memo[num] != -1 && cnt > memo[num]) return memo[num];
if(num%6 == 0) return ret = min(dp(num/3, cnt+1), min(dp(num/2, cnt+1), dp(num-1, cnt+1))) + 1;
if(num%3 == 0) return ret = min(dp(num/3, cnt+1), dp(num-1, cnt+1)) + 1;
if(num%2 == 0) return ret = min(dp(num/2, cnt+1), dp(num-1, cnt+1)) + 1;
return ret = dp(num-1, cnt+1) + 1;
}
int main() {
/* 입력 */
cin >> N;
/* 풀이 */
memset(memo, -1, sizeof(memo));
/* 출력 */
cout << dp(N, 0) << '\n';
}
이번 문제는 딱히 논리라고 할 것은 없고 그대로 부딪혔다.
3으로 나눠지면 3으로 나눠보고, 2로 나눠지면 2로 나눠보고, 안되면 1을 빼고 count했다.
단, 시간제한이 있으므로 memoization으로 이미 계산한 값은 다시 계산하지 않았다.
그런데 풀고나서 정답을 찾아보니 bottom-top 형식으로 N까지 채워나가는 방식이 더 빨랐다...
백준 [01463] 1로 만들기
https://www.acmicpc.net/problem/1463