双重河内塔II

双重河内塔问题

又称:双重汉诺塔问题

这是第二篇

这是《具体数学:计算机科学基础(第2版)》中的一道课后习题
这道题也是挺有意义的,但是百度上一篇C/C++代码都没有
看了书的答案,和百度上唯一一篇文章黄大佬的思路,还是没看懂
最后还是自己花了一个晚上推出来思路,然后写出了b问题的代码
我打算写三篇随笔来讲这个问题,这是第二篇

双重河内塔包含 2n 个圆盘,它们有 n 种不同的尺寸,每一种尺寸的圆盘有两个。如通常那样,要求每次只能移动一个圆盘,且不能把较大的圆盘放在较小的圆盘上面。

a 如果相同尺寸的圆盘是相互不可区分的,要把一个双重塔从一根桩柱移动到另一根桩柱需要移动多少次?

b 如果在最后的排列中要把所有同样尺寸的圆盘恢复成原来的从上到下的次序,需要移动多少次?
提示:这是一个难题,实在应该是个“附加题”。

本文章针对b问题
总共有2n个圆盘在A柱上,我们现在需要把它们全部挪到C柱上
b问题的限制条件是:最后保持原来的上下次序不改变
所以隐藏含义就是:移动的过程中,同一大小型号的盘子的上下次序无所谓

对于这类递归的题目,我们应该先找规律,然后再推出递归式,写出代码

PS:这边我们要写的程序是,输出每一个步骤,例如将几号盘从几号柱子移动到几号柱子

思路推进

n = 1 时,执行以下算法:

  1. 第 n(上) 圆盘从 A 移到 C,需 1 步;
  2. 第 n(下) 圆盘从 A 移到 B,需 1 步;
  3. 第 n(上) 圆盘从 C 移到 B,需 1 步。




我们将圆盘从上到下依次按照1~2n进行编号
我们中间的输出步骤过程暂时不考虑2k-1和2k的差别
先简化难度,将2k-1和2k看成一样的

(但是最后上下顺序还是原来的,最后总步数不影响,只是简化中间输出过程)

输出中间步骤时统一输出"k号型盘子"
第三篇文章中会讲到区别2k-1与2k的做法

因为顺序要和初始的一样
第2n号圆盘应该,被放在第C柱的最低端
然后再放上2n-1号盘子
最后再将1~(2n-2)号盘子全部移到C柱上
但是我们怎么确定,移动1~(2n-2)号盘子就是顺序的呢?
因为执行一次\(A_{n-1}\)操作后,只有最下面两个盘子的顺序会互换
因为\(A_n\)中需要执行两次\(A_{n-1}\)操作
所以执行两次之后就回到了原来的顺序
具体证明会在第三篇中讲到

1.第 1~n-1 圆盘从 A 移到 B,需 \(A_{n-1}\) 步;
2.第 n(上) 圆盘从 A 移到 C,需 1 步;
3.第 1~n-1 圆盘从 B 移到 C,需 \(A_{n-1}\) 步;
4.第 n(下) 圆盘从 A 移到 B,需 1 步;
5.第 1~n-1 圆盘从 C 移到 A,需 \(A_{n-1}\) 步;
6.第 n(上) 圆盘从 C 移到 B,需 1 步;
7.第 1~n-1 圆盘从 A 移到 B,需 \(A_{n-1}\) 步。





代码实现

#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) {
	if(n==1) {
		Move(n,spos,epos);
		Move(n,spos,epos);
	} else {
		AHanio(n-1,spos,epos,tpos);
		Move(n,spos,epos);
		Move(n,spos,epos);
		AHanio(n-1,tpos,spos,epos);
	}
}
void BHanio(int n,char spos,char tpos,char epos) {
	if(n==1) {
		Move(n,spos,tpos);
		Move(n,spos,epos);
		Move(n,tpos,epos);
	} else {
		AHanio(n-1,spos,tpos,epos);
		Move(n,spos,tpos);
		AHanio(n-1,epos,spos,tpos);
		Move(n,spos,epos);
		AHanio(n-1,tpos,epos,spos);
		Move(n,tpos,epos);
		AHanio(n-1,spos,tpos,epos);
	}
}
int main() {
	int n;
	scanf("%d",&n);
	BHanio(n,'A','C','B');
	printf("最后总的步数为%d步\n",step);
}
/*
8 3
16 11
32 27
64 59
*/

封闭式推导

根据递归式,我们可以得出

我们设最终完成2n个圆盘的移动所需要的总步数为\(B_n\)
第一篇文章中,已知问题a对应的\(A_n=2^{n+1}-2\)

\(\begin{align} &B_n=A_{n-1}+1+A_{n-1}+1+A_{n-1}+1+A_{n-1}\\ &=4\times A_{n-1}+3\\ &=4\times (2^{n}-2)+3\\ &=2^{n+2}-5\\ \end{align}\)

posted @ 2020-11-10 23:49  幽灵轩  阅读(289)  评论(1编辑  收藏  举报