바킹독의 실전 알고리즘 강의를 참고하여 공부한 내용입니다.
https://youtu.be/8vDDJm5EewM?si=SUESkBHrCnthuNwM
https://www.acmicpc.net/problem/16947
아래의 조건을 지키며 n개의 원판을 기둥1에서 기둥3으로 옮기는 것을 의미한다.
n개의 원판을 옮기기 위해서는, 결국 n번 원판을 기둥1에서 기둥3으로 옮겨야 한다.
이를 위해서는 n-1번까지의 원판들이 모두 기둥2로 옮겨져 있어야 한다.
=> 즉, n-1개의 원판을 이동할 수 있으면 n개의 원판도 이동할 수 있다.
원판 n개를 a번 기둥에서 b번 기둥으로 옮기는 방법을 출력하는 함수를 짜면 해당 문제를 해결할 수 있다.
if (n == 1)
{
cout << a << ' ' << b << '\n';
return;
}
func(a, 6 - a - b, n - 1);
cout << a << ' ' << b << '\n';
func(6 - a - b, b, n - 1);
#include <iostream>
#include <cmath>
using namespace std;
// 원판 n개를 a번 기둥에서 b번 기둥으로 옮기는 방법을 출력하는 함수
void func(int a, int b, int n)
{
if (n == 1)
{
cout << a << ' ' << b << '\n';
return;
}
func(a, 6 - a - b, n - 1);
cout << a << ' ' << b << '\n';
func(6 - a - b, b, n - 1);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int num = 0;
cin >> num;
cout << (int)(pow(2, num) - 1) << '\n';
func(1, 3, num);
return 0;
}
귀납적으로 사고해야 하지만 나는 절차지향적 인간이기 때문에.. 원판이 3개라고 가정하고 코드 흐름을 적어보았다.
T(n) = 2 * T(n-1) + 1
한 기둥에 차례로 꽂혀있는 n개의 원판을 특정 기둥으로 옮기는 횟수가 T(n)이라고 가정하면, n-1개의 원판을 옮기는 횟수는 T(n-1)이다. 해당 코드에서 n-1개의 원판을 경유지 기둥으로 이동하고, 가장 큰 원판을 목적지 기둥으로 이동하고, 경유지 기둥의 n-1개의 원판을 다시 목적지 기둥으로 이동한다. 따라서 T(n) = 2 * T(n-1) + 1 가 성립한다.
=> T(n)에 차례로 대입하며 풀어보면, 2^n -1 이라는 규칙을 발견할 수 있다.