https://www.acmicpc.net/problem/27743
27743번: 어려운 하노이 탑
하노이 탑은 다음 규칙을 지키면서, 첫 번째 막대기에 꽂힌 원판들을 그 순서 그대로 세 번째 막대기로 옮기는 놀이다. 한 번에 1개의 원판만 옮길 수 있다. 가장 위에 있는 원판만 이동할 수 있
www.acmicpc.net
N=4, M=3 정도인 상황을 시뮬레이션 돌려보면 감을 잡을 수 있습니다.
우선, 각 기둥을 왼쪽에서부터 peg 1, 2, 3 이라고 하겠습니다.
그리고 크기 N의 원판이 번호 1부터 M에 대해 내림차순으로 쌓인 상황을 N↓로 표현하겠습니다.
다시 말해 문제에서 주어진 초기 상황이 다음과 같이 표현된다는 뜻입니다.
1↓2↓⋮N↓peg1peg2peg3
N=4,M>1 일 때 원판을 최소한으로 움직이는 상황을 요약하면 다음의 흐름입니다.
(1↓2↓3↓4↓peg1peg2peg3)→(1↓2↓4↓3↑peg1peg2peg3)→(1↓2↓4↑3↑peg1peg2peg3)
→(1↓2↓3↓4↑peg1peg2peg3)→(1↓2↓3↓4↓peg1peg2peg3)→(1↓2↓3↓4↓peg1peg2peg3)
바로 마지막 부분만 살펴보면 DP를 쓸 수 있을 것처럼 보이는데, 일반적인 하노이 탑 문제를 풀 때처럼 접근하면 될까요?
이 부분을 살펴보겠습니다.
(1↓2↓3↓4↓peg1peg2peg3)→(1↓2↓4↓3↑peg1peg2peg3)
무슨 과정을 거쳐서 이렇게 된 걸까요?
우선 화살표의 방향을 봤을 때, 크기 3의 원판은 1번씩만 움직였고, 크기 2, 1의 원판들은 짝수번 움직였다고 짐작할 수 있습니다.
실제로 이동과정이 다음과 같습니다.
(1↓2↓3↓4↓peg1peg2peg3)→(2↓3↓4↓1↑peg1peg2peg3)→(3↓4↓2↑1↑peg1peg2peg3)
→(3↓1↓4↓2↑peg1peg2peg3)→(1↓4↓2↑3↑peg1peg2peg3)→(1↑4↓2↑3↑peg1peg2peg3)
→(1↑2↓4↓3↑peg1peg2peg3)→(1↓2↓4↓3↑peg1peg2peg3)
이때 특히, 크기 2인 원반이 2번, 크기 1인 원반이 4번 움직인다는 걸 관찰할 수 있습니다.
이를 N 과 M 에 대해 일반화하면,
크기 N−1 인 원반이 1번, 크기 N−2 인 원반이 2번, ⋯, 크기 1 인 원반이 2N−2번 움직입니다.
크기 별로 원반은 M 개 있으므로, 이 과정에서 이동횟수는 M×(2N−1−1) 입니다.
이상의 과정을 B 과정이라고 하면, B 과정은 2회 반복됩니다. 그리고 B 과정의 이동횟수는 BN,M=M×(2N−1−1) 라고 하겠습니다.
전체 최소 이동횟수를 AN,M라고 하면 M>1 일 때,
AN,M=BN,M+M+BN,M+M+AN−1,M
라는 식이 성립합니다.
초기항이
A1,M=2M−1
임을 이용하여 점화식을 풀면
AN,M=2M×(2N−1)−1
이 나옵니다.
한편, 언뜻 생각하면 위 식에 M=1을 대입하여도 올바른 식이 나와야 할 것 같지만, M=1의 경우 오름차순과 내림차순이 올바르게 적용되기 힘듭니다.
그 말은 B 과정이 최소 이동횟수에 포함되지 않는다는 뜻이며,
따라서 M>1일 때의 점화식이 성립하지 않고,
AN,1=2N−1
이 됩니다.
요약하면, 다음을 구현하면 되겠습니다.
AN,M={2N−1,if M=12M(2N−1)−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)
'분할 정복을 이용한 거듭제곱 > 정수 거듭제곱' 카테고리의 다른 글
백준 30354번: Sum of Product of Binomial Coefficients (1) | 2023.11.09 |
---|---|
백준 30169번: Primes and Multiplication (1) | 2023.09.30 |
백준 28294번: 프랙탈 (0) | 2023.09.02 |
백준 27875번: 파이파이 (0) | 2023.09.01 |
백준 27318번: 세상에서 가장 달달한 디저트 만들기 (integer) (0) | 2023.03.14 |