[백준 1463] 1로 만들기 (C#, C++) - soo:bak
작성일 :
문제 링크
설명
정수 N
을 정해진 세 가지 연산만을 사용하여 1
로 만들 때 필요한 최소 연산 횟수를 구하는 동적 프로그래밍 문제입니다.
사용 가능한 연산은 다음과 같습니다:
- 현재 숫자에서
1
을 뺍니다. - 현재 숫자가
2
로 나누어떨어질 경우2
로 나눕니다. - 현재 숫자가
3
으로 나누어떨어질 경우3
으로 나눕니다.
이 연산들을 조합해 N
을 1
로 만드는 가장 적은 횟수를 찾아야 합니다.
핵심은 각 숫자에 대해 어떤 연산을 선택할지 판단하는 과정입니다.
접근법
작은 수부터 차례대로 최소 연산 횟수를 계산하여 저장하는 상향식 동적 프로그래밍 방식으로 풀이할 수 있습니다.
1
은 이미 목표이므로 연산이 필요 없습니다.2
부터N
까지, 각 수를1
로 만들기 위한 최소 연산 횟수를 구합니다.
어떤 정수를 1
로 만들기 위해 고려할 수 있는 세 가지 경우는 다음과 같습니다:
- 바로 이전 수에서
1
을 빼는 경우
현재 수 - 1
을1
로 만드는 데 필요한 최소 연산 횟수에1
을 더합니다.
2
로 나누어떨어지는 경우
현재 수 ÷ 2
를1
로 만드는 데 필요한 최소 연산 횟수에1
을 더합니다.
3
으로 나누어떨어지는 경우
현재 수 ÷ 3
를1
로 만드는 데 필요한 최소 연산 횟수에1
을 더합니다.
이 세 경우 중 가장 적은 연산 횟수를 선택하여 현재 수에 대한 값을 기록하면 됩니다.
예를 들어, 3
을 1
로 만드는 최적 경로는 3으로 나누기
연산 1
회 입니다.
9
를 1
로 만드는 최적의 경로는 3으로 나누기
연산 1
회를 통해 3
으로 만든 후,
위에서 구한 3
이 1
이 되는 경로에서 필요한 최소한의 연산 1
회를 더한 경로 9 → 3 → 1
즉, 2
회의 연산이 필요한 경로가 최적의 경로입니다.
마찬가지로, 10
을 1
로 만드는 최적 경로는 10 → 9 → 3 → 1
(1 빼기
, 3으로 나누기
, 3으로 나누기
)로 3
회입니다.
따라서, 작은 수 부터 최적의 경로를 구하는 과정을 반복하면 전체적으로 시간 복잡도 O(N)
으로 풀이할 수 있습니다.
Code
[ C# ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;
class Program {
static void Main() {
int n = int.Parse(Console.ReadLine());
var dp = new int[n + 1];
for (int i = 2; i <= n; i++) {
int min = dp[i - 1] + 1;
if (i % 2 == 0) min = Math.Min(min, dp[i / 2] + 1);
if (i % 3 == 0) min = Math.Min(min, dp[i / 3] + 1);
dp[i] = min;
}
Console.WriteLine(dp[n]);
}
}
[ C++ ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int cache[1'000'001] = {0, };
int num; cin >> num;
for (int i = 2; i <= num; i++) {
int res = cache[i - 1] + 1;
if (i % 2 == 0) res = min(res, cache[i / 2] + 1);
if (i % 3 == 0) res = min(res, cache[i / 3] + 1);
cache[i] = res;
}
cout << cache[num] << "\n";
return 0;
}