본문 바로가기

알고리즘 문제 풀어보기

[백준 1914번] 하노이 탑 문제

하노이 탑


문제


세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로  옮기려 한다.

  1. 한 번에 한 개의 원판만을 다른 탑으로 옮길 수 있다.
  2. 쌓아 놓은 원판은 항상 위의 것이 아래의 것보다 작아야 한다.(중간 과정 역시 그래야함)

이 작업을 수행하는데 필요한 이동순서를 출력하는 프로그램을 작성하라

아래 그림은 원판이 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, 123));
        hanoi_tower(n, 123);
    }
    else {
        printf("%Lf\n", hanoi_count(n, 123));
    }
 
    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, 123);
    }
    return 0;
}
cs


코드를 찾아서 붙히는데 (출처: Baekjoon Online - jumpingz 님) 2^n 에 - 1을 한 값을 구하는 부분

(코드: 33째 줄)은 왜 저렇게 48을 빼는건지는 도통 모르겠다...


나는 그저 하노이탑 문제를 풀고 싶었을 뿐인데 이상하게 빅인티저가 메인이 되버린 문제였다.