하노이 탑
문제
세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로 옮기려 한다.
- 한 번에 한 개의 원판만을 다른 탑으로 옮길 수 있다.
- 쌓아 놓은 원판은 항상 위의 것이 아래의 것보다 작아야 한다.(중간 과정 역시 그래야함)
이 작업을 수행하는데 필요한 이동순서를 출력하는 프로그램을 작성하라
아래 그림은 원판이 5개인 경우의 예시이다.
입력
첫째 줄에 첫 번째 장대에 쌓인 원판의 개수 N (1 ≤ N ≤ 100)이 주어진다.
출력
첫째 줄에 옮긴 횟수 K를 출력한다.
N이 20 이하인 입력에 대해서는 두 번째 줄부터 수행 과정을 출력한다. 두 번째 줄부터 K개의 줄에 걸쳐 두 정수 A B를 빈칸을 사이에 두고 출력하는데, 이는 A번째 탑의 가장 위에 있는 원판을 B번째 탑의 가장 위로 옮긴다는 뜻이다. (N이 20보다 큰 경우에는 과정은 출력할 필요가 없다.)
예제입력
3
예제출력
7
1 3
1 2
3 2
1 3
2 1
2 3
1 3
처음엔 간단하게 생각해서 하노이 결과 출력 함수를 만들고 전역변수를 써서 옮긴 횟수를 나타내려 했다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #include <stdio.h> #include <math.h> #pragma warning(disable: 4996) long long K = 0; //전역변수 (옮긴 횟수를 나타내기 위함) //막대 from에 쌓여있는 원판을 tmp를 이용해 to로 옮기는 것!! void hanoi_tower(int N, int from, int tmp, int to) { //from: 시작점, tmp: 중간지점, to: 도착지점 if (N <= 20) { if (N == 1) { printf("%d %d\n", from, to); } else { hanoi_tower(N - 1, from, to, tmp); //1. 1개(제일 밑)를 제외한 모든 원판을 to를 이용해 tmp로 옮긴다. printf("%d %d\n", from, to); //2. from막대에 있는 1개(제일 바닥에 있는 원판)를 to로 옮긴다. hanoi_tower(N - 1, tmp, from, to); //3. tmp에 있는 제일 밑 원판을 제외한 모든 원판을 from을 이용해 to로 옮긴다. } } } long double hanoi_count(int N, int from, int tmp, int to) { if (N == 1) K++; else { hanoi_count(N - 1, from, to, tmp); K++; hanoi_count(N - 1, tmp, from, to); } return K; } int main() { int n; scanf("%d", &n); if (n <= 20) { printf("%Lf\n", hanoi_count(n, 1, 2, 3)); hanoi_tower(n, 1, 2, 3); } else { printf("%Lf\n", hanoi_count(n, 1, 2, 3)); } return 0; } | cs |
이렇게 코딩을 짜면 문제는 N이 높은 수가 되버리면 K값을 나타내지 못한다는 것이다...
확인해보니 옮기는 횟수 K는 이라는 식과 같이 나온다. (n=3일때 K=7, n=4일때 K=15)
즉 문제에서 요구하는 원판의 최대갯수일 때 옮기는 횟수 까지의 결과값을 나타낼 수 있어야 한다.
(unsigned long long 타입도 한계가 있다.)
여기에서 빅인티저라는 것을 적용해야 한다고 말하는 사람들이 있어서 인터넷에서 빅인티저 구현법을 여차저차 찾아보며 적용해보았다.
(빅인티저를 직접 구현하는건 너무 어려운 거 같다 ㅜㅜ)
찾아보니 무슨 문자열에 숫자를 넣어 큰 정수값을 문자열로 나타내는 모양이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <stdio.h> #include <math.h> #include <string.h> #pragma warning(disable: 4996) #define MAX 1000000 //막대 from에 쌓여있는 원판을 tmp를 이용해 to로 옮기는 것!! void hanoi_tower(int N, int from, int tmp, int to) { //from: 시작점, tmp: 중간지점, to: 도착지점 if (N <= 20) { if (N == 1) { printf("%d %d\n", from, to); } else { hanoi_tower(N - 1, from, to, tmp); //1. 1개(제일 밑)를 제외한 모든 원판을 to를 이용해 tmp로 옮긴다. printf("%d %d\n", from, to); //2. from막대에 있는 1개(제일 바닥에 있는 원판)를 to로 옮긴다. hanoi_tower(N - 1, tmp, from, to); //3. tmp에 있는 제일 밑 원판을 제외한 모든 원판을 from을 이용해 to로 옮긴다. } } } int main() { int n = 0; char k_result[MAX]; long double pow_d = 2.0, tmp; scanf("%d", &n); tmp = pow(pow_d, n); //2의 n승한 값을 tmp변수에 저장 sprintf(k_result, "%.0Lf",tmp); //숫자나 문자 등을 문자열에 넣을 때 sprintf !! int size = strlen(k_result); k_result[size - 1] = (char)(((int)(k_result[size - 1] - 48) - 1) + 48); //(int)(k_result[size - 1] - 48): 이부분에 48을 왜 마이너스 하는건지 모르것다... printf("%s\n", k_result); if (n <= 20) { hanoi_tower(n, 1, 2, 3); } return 0; } | cs |
코드를 찾아서 붙히는데 (출처: Baekjoon Online - jumpingz 님) 2^n 에 - 1을 한 값을 구하는 부분
(코드: 33째 줄)은 왜 저렇게 48을 빼는건지는 도통 모르겠다...
나는 그저 하노이탑 문제를 풀고 싶었을 뿐인데 이상하게 빅인티저가 메인이 되버린 문제였다.
'알고리즘 문제 풀어보기' 카테고리의 다른 글
5x5 배열에 직각삼각형 만들기 (0) | 2019.01.30 |
---|---|
최대공약수, 최소공배수 구하기 알고리즘(+약수 구하기) (0) | 2019.01.26 |
[백준 1475] 방 번호 문제 (0) | 2019.01.24 |
[백준 8958번] OX퀴즈 (0) | 2019.01.24 |
[백준 1152번] 단어의 개수 구하기 (문자열 문제) (0) | 2019.01.22 |