https://www.acmicpc.net/problem/1463
1463번: 1로 만들기
첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.
www.acmicpc.net
문제 해결 과정
착안
자연수 $n=1, 2, ..., N$ 에 대해 $n=1$ 부터 반복문을 통해 $n$의 값을 증가시키면서 $3n, 2n, n+1$ 에 대한 연산 횟수의 값을 동적계획법(dynamic programming)을 통해 최솟값으로 갱신함으로써 문제를 해결하고자 했다.
구현
주어지는 수 $N$ 보다 큰 수에 대해서는 계산할 필요가 없으므로, $n=i$ 일 때, $3i, 2i, i+1$ 의 값이 $N$ 보다 작거나 같을 때에만 계산을 하도록 하였다. $3i, 2i, i+1$ 의 연산 횟수가 이미 계산되어있는 경우에는 최솟값으로 갱신, 계산되어있지 않은 경우에는 $i$ 의 연산 횟수에 1을 더한 값으로 초기화하도록 구현하였다.
[스포 주의] 아래 '더보기'를 누르면 코드가 나오니 주의하세요~
#include <iostream>
#include <algorithm>
using namespace std;
char Counts[1000001];
int main()
{
int N;
cin >> N;
int i = 0;
while (i++ < N)
{
char cnt = Counts[i] + 1;
if (3 * i <= N)
Counts[3 * i] = Counts[3 * i] ? min(Counts[3 * i], cnt) : cnt;
if (2 * i <= N)
Counts[2 * i] = Counts[2 * i] ? min(Counts[2 * i], cnt) : cnt;
Counts[i + 1] = Counts[i + 1] ? min(Counts[i + 1], cnt) : cnt;
}
cout << (int)Counts[N] << "\n";
return 0;
}
그런데 이렇게 문제를 해결하고 나서 문제를 효율적으로 해결한 다른 분들의 코드를 살펴보니 정말 어이가 없을 정도로 간단히 해결하는 것을 확인하고 흥건하게 지렸다(아래 코드 참고).
최적화 된 코드를 이해하기 위해서는 문제에서 제시한 조건이 다음의 특징을 가진다는 것을 이해해야 한다.
- 주어지는 수를 최소 연산으로 1로 만들기 위해서는 3 또는 2로 나누는 연산을 최대한 많이 수행해야 한다.
- 주어지는 수를 1로 만드는 과정에서, 3 또는 2 중 어떤 수로 나누는 것이 더 유리한지는 계산해야만 알 수 있다.
- 주어진 숫자를 3 또는 2의 배수로 만들기 위한 연산 횟수는 각각 3, 또는 2로 나눈 나머지 연산으로 표현 가능하다.
[스포 주의] (최적화 코드) 아래 '더보기'를 누르면 코드가 나오니 주의하세요~
#include <iostream>
#include <algorithm>
using namespace std;
int Counts(int n)
{
// n == 1 또는 n == 0 : 0을 반환
// n >= 2 인 경우 : 1씩 줄여서 2의 배수로 만들어 2로 나누는 연산 횟수와
// 1씩 줄여서 3의 배수로 만들어 3으로 나누는 연산 횟수 중에
// 더 작은 수를 택하여 반환
return n <= 1 ? 0 : 1 + min(Counts(n / 2) + n % 2, Counts(n / 3) + n % 3);
}
int main()
{
int N;
cin >> N;
cout << Counts(N) << "\n";
return 0;
}
실행 결과
'C++ 코딩 문제 풀이 > 백준' 카테고리의 다른 글
[Baekjoon] 1966번: 프린터 큐 (0) | 2023.06.17 |
---|---|
[Baekjoon] 9663번: N-Queen (0) | 2023.06.16 |
[Baekjoon] 11866번: 요세푸스 문제 0 (0) | 2023.06.14 |
[Baekjoon] 11047번: 동전 0 (0) | 2023.06.13 |
[Baekjoon] 1874번: 스택 수열 (0) | 2023.06.12 |
댓글