Loading [MathJax]/jax/output/CommonHTML/jax.js
본문 바로가기

분할 정복을 이용한 거듭제곱/정수 거듭제곱

백준 27743번: 어려운 하노이 탑

https://www.acmicpc.net/problem/27743

 

27743번: 어려운 하노이 탑

하노이 탑은 다음 규칙을 지키면서, 첫 번째 막대기에 꽂힌 원판들을 그 순서 그대로 세 번째 막대기로 옮기는 놀이다. 한 번에 1개의 원판만 옮길 수 있다. 가장 위에 있는 원판만 이동할 수 있

www.acmicpc.net


N=4, M=3 정도인 상황을 시뮬레이션 돌려보면 감을 잡을 수 있습니다.

 

우선, 각 기둥을 왼쪽에서부터 peg 1, 2, 3 이라고 하겠습니다.

 

그리고 크기 N의 원판이 번호 1부터 M에 대해 내림차순으로 쌓인 상황을 N로 표현하겠습니다.

 

다시 말해 문제에서 주어진 초기 상황이 다음과 같이 표현된다는 뜻입니다.

12Npeg1peg2peg3


N=4,M>1 일 때 원판을 최소한으로 움직이는 상황을 요약하면 다음의 흐름입니다.

(1234peg1peg2peg3)(1243peg1peg2peg3)(1243peg1peg2peg3)

(1234peg1peg2peg3)(1234peg1peg2peg3)(1234peg1peg2peg3)

바로 마지막 부분만 살펴보면 DP를 쓸 수 있을 것처럼 보이는데, 일반적인 하노이 탑 문제를 풀 때처럼 접근하면 될까요?

 

이 부분을 살펴보겠습니다.

(1234peg1peg2peg3)(1243peg1peg2peg3)

무슨 과정을 거쳐서 이렇게 된 걸까요?

 

우선 화살표의 방향을 봤을 때, 크기 3의 원판은 1번씩만 움직였고, 크기 2, 1의 원판들은 짝수번 움직였다고 짐작할 수 있습니다.

 

실제로 이동과정이 다음과 같습니다.

(1234peg1peg2peg3)(2341peg1peg2peg3)(3421peg1peg2peg3)

(3142peg1peg2peg3)(1423peg1peg2peg3)(1423peg1peg2peg3)

(1243peg1peg2peg3)(1243peg1peg2peg3)

이때 특히, 크기 2인 원반이 2번, 크기 1인 원반이 4번 움직인다는 걸 관찰할 수 있습니다.

 

이를 NM 에 대해 일반화하면,

 

크기 N1 인 원반이 1번, 크기 N2 인 원반이 2번, , 크기 1 인 원반이 2N2번 움직입니다.

 

크기 별로 원반은 M 개 있으므로, 이 과정에서 이동횟수는 M×(2N11) 입니다.

 

이상의 과정을 B 과정이라고 하면, B 과정은 2회 반복됩니다. 그리고 B 과정의 이동횟수는 BN,M=M×(2N11) 라고 하겠습니다.


전체 최소 이동횟수를 AN,M라고 하면 M>1 일 때,

AN,M=BN,M+M+BN,M+M+AN1,M

라는 식이 성립합니다.

 

초기항이

A1,M=2M1

임을 이용하여 점화식을 풀면

AN,M=2M×(2N1)1

이 나옵니다.


한편, 언뜻 생각하면 위 식에 M=1을 대입하여도 올바른 식이 나와야 할 것 같지만, M=1의 경우 오름차순과 내림차순이 올바르게 적용되기 힘듭니다.

 

그 말은 B 과정이 최소 이동횟수에 포함되지 않는다는 뜻이며,

 

따라서 M>1일 때의 점화식이 성립하지 않고,

AN,1=2N1

이 됩니다.


요약하면, 다음을 구현하면 되겠습니다.

AN,M={2N1,if M=12M(2N1)1,otherwise

다음은 제 코드입니다.

#include <stdio.h>
#define mod 1000000007ull
typedef unsigned long long ull;

ull power_modular (ull X, ull Y);

int main() {
    
    long long N, M;
    scanf("%lld %lld", &N, &M);
    
    ull answer;
    if (M == 1) {
        answer = power_modular(2, N) + mod - 1;
    }
    else {
        answer = M * (power_modular(2, N + 1) + mod - 2) + mod - 1;
    }
    printf("%llu", answer % mod);
    
    return 0;
}

ull power_modular (ull X, ull Y) {
    ull result = 1;
    while (Y) {
        if (Y % 2) {
            result = result * X % mod;
        }
        X = X * X % mod;
        Y /= 2;
    }
    return result;
}

(C11, 0ms, 1116KB, 제출번호 67336747)