双重河内塔III
双重河内塔问题
又称:双重汉诺塔问题
这是第三篇
这是《具体数学:计算机科学基础(第2版)》中的一道课后习题
这道题也是挺有意义的,但是百度上一篇C/C++代码都没有
看了书的答案,和百度上唯一一篇文章黄大佬的思路,还是没看懂
最后还是自己花了一个晚上推出来思路,然后写出了b问题的代码
我打算写三篇随笔来讲这个问题,这是第三篇
双重河内塔包含 2n 个圆盘,它们有 n 种不同的尺寸,每一种尺寸的圆盘有两个。如通常那样,要求每次只能移动一个圆盘,且不能把较大的圆盘放在较小的圆盘上面。
a 如果相同尺寸的圆盘是相互不可区分的,要把一个双重塔从一根桩柱移动到另一根桩柱需要移动多少次?
b 如果在最后的排列中要把所有同样尺寸的圆盘恢复成原来的从上到下的次序,需要移动多少次?
提示:这是一个难题,实在应该是个“附加题”。
本文章针对b问题
我们将圆盘从上到下按1~2n编号
上一篇我们写了一个中间输出不区分2k-1和2k圆盘的程序
今天这篇主要写一个中间输出区分2k-1和2k圆盘的程序
以及详细证明一次An操作,不改变1~(2n-2)圆盘的上下顺序,
但是改变2n-1和2n的上下顺序
我们先看完成一次\(B_n\)操作的具体步骤
- 先将\(1\sim (2n-2)\)圆盘挪到C柱
这边我们先不考虑它们的上下顺序是否改变
- 将\(2n-1\)挪到B柱
- 将\(1\sim (2n-2)\)圆盘挪到B柱
- 将\(2n\)移到C柱
- 将\(1\sim (2n-2)\)圆盘挪到A柱
- 将\(2n-1\)挪到C柱
- 将\(1\sim (2n-2)\)圆盘挪到C柱
中间有四次\(A_n\)操作
完成\(A_n\)操作的具体步骤
- 先将\(1\sim (2n-2)\)圆盘挪到B柱
这边我们先不考虑它们的上下顺序是否改变
2.将\(2n-1\)和\(2n\)依次挪到C柱
此时\(2n-1\)和\(2n\)的上下次序改变
- 先将\(1\sim (2n-2)\)圆盘挪到C柱
这边我们可以发现进行了两次\(A_{n-1}\)操作
可以发现一次\(A_n\)操作交换一次2n-1和2n的上下次序
同理一次\(A_{n-1}\)操作交换一次2n-3和2n-2的上下次序
但是\(A_{n-1}\)操作被执行了两次,
即说明1~(2n-2)圆盘上下顺序不改变
用\(H_{i}\)来表示2i-1和2i的上下次序
1代表没有交换
-1代表交换
易得上一个层比下一个层多移动了两次
\(\begin{align} & H_n=-1\\ & H_{n-1}=(-1)^{2}\\ & H_{n-2}=(-1)^{4}\\ &\because H_{n-i}=(-1)^{2}\times H_{n-i+1},2\leq i \leq n-1,i\in N^{*}\\ &\therefore H_{n-i}=(-1)^{2i} \\ &\therefore H_{i}=(-1)^{2(n-i)},1\leq i \leq n-1,i\in N^{*}\\ \end{align}\)
所以每次进行Ahanio操作的时候只需要记录
最底下两个盘是否被交换即可,我们写程序就直接用0,1来记录An状态
(原本想用异或来偷懒,后来忘记了,就懒得改程序了)
代码实现
#include<cstdio>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
int step;
void Move(int id,char from,char to) {
printf("第%d步:将%d号盘子%c-->%c\n",++step,id,from,to);
return ;
}
void AHanio(int n,char spos,char tpos,char epos,int f) {
if(n==1) {
if(f) {
Move(2,spos,epos);
Move(1,spos,epos);
} else {
Move(1,spos,epos);
Move(2,spos,epos);
}
} else {
AHanio(n-1,spos,epos,tpos,0);
if(f) {
Move(2*n,spos,epos);
Move(2*n-1,spos,epos);
} else {
Move(2*n-1,spos,epos);
Move(2*n,spos,epos);
}
AHanio(n-1,tpos,spos,epos,1);
}
}
void BHanio(int n,char spos,char tpos,char epos) {
if(n==1) {
Move(1,spos,tpos);
Move(2,spos,epos);
Move(1,tpos,epos);
} else {
AHanio(n-1,spos,tpos,epos,0);
Move(2*n-1,spos,tpos);
AHanio(n-1,epos,spos,tpos,1);
Move(2*n,spos,epos);
AHanio(n-1,tpos,epos,spos,0);
Move(2*n-1,tpos,epos);
AHanio(n-1,spos,tpos,epos,1);
}
}
int main() {
int n;
scanf("%d",&n);
BHanio(n,'A','C','B');
printf("最后总的步数为%d步\n",step);
}
/*
8 3
16 11
32 27
64 59
*/