## [$AcWing$ $432$. $Hanoi$双塔问题](https://www.acwing.com/problem/content/434/) ### 一.汉若单塔(也称经典汉若塔问题) 首先,我们先画个 $n=3$ 图: ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/{year}/{month}/{md5}.{extName}/202309220752010.png) 要把 $A$ 柱上的 **圆盘** 移到 $C$ 柱上,要求无论在哪个柱子上的圆盘都是大的在下面,小的在上面。求从 $A$ 柱上的圆盘移到 $C$ 柱上要多少步? #### 1. 实践 遇到这种问题,首先想到的是先模拟一次,如下图: ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/{year}/{month}/{md5}.{extName}/202309220753173.png) 怎么样?看完过程,是不是清晰了许多?全过程是这样的:
         步数                 操作        
1 ① A to C
2 ② A to B
3 ① C to B
4 ③ A to C
5 ① B to A
6 ② B to C
7 ① A to C
#### 2. 探索 >**解释** ① :最上面的第$1$个圆饼 ② :中间的第$2$个圆饼 ③ :最下面的第$3$个圆饼 总共$7$步。仔细观察我们可以发现,移动的盘子是对称的,如上表:$①②①③①②①$。 以$③$为点 ,左右对称。再看看右边$①②①$,也是对称的,这是 $n=2$ 时移动的步数。我们是不是可以推测出如果 $n=4$ 时,$①②①③①②①④①②①③①②①$是移动顺序呢?事实证明的确如此。移动顺序总是上一个序列加上新的盘子再加上一个序列。 > **注** > 我终于懂了汉诺塔,真是太$TM$简单了~ 由此可得递推公式: $$\large f[i] =f[i−1] ∗2+1$$ [刘老师视频讲座](https://www.bilibili.com/video/BV1Rq4y1v7sC) #### 3. 运用 如何运用到$c++$里面呢? ```cpp {.line-numbers} int f[10010]; int n; scanf ("%d", &n); for (int i = 1 ; i <= n ; i++) f[i] = f[i-1] * 2 + 1; printf ("%d\n", f[n]); ``` 这就是主程序。 #### 4. 思考 如何输出中间的步骤?可在评论区回答 `or` 讨论! ### 二.汉若双塔 #### 1. 认识 我们来看看汗若双塔长什么样子吧: ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/{year}/{month}/{md5}.{extName}/202309221022246.png) 他就是每种型号多了$1$个盘子。移动起来的步骤也就乘$2$了。 #### 2. 实现 ```cpp {.line-numbers} int f[10010]; int n; scanf ("%d", &n); for (int i = 1 ; i <= n ; i++) f[i] = f[i-1] * 2 + 1; printf ("%d\n", f[n] * 2); ``` ### 三.加强 当数据 $n<=200$ 时,我们应该怎么做? $answer$ :当然是 **无符号** $long$ $long$ 啦。 $NO!NO!NO!$ 我们应该开高精度,这才是正解!!! 康康 $code$ : ```cpp {.line-numbers} #include using namespace std; const int N = 100010; // 加法高精度 int a[N], b[N]; int al, bl; void add(int a[], int &al, int b[], int &bl) { int t = 0; al = max(al, bl); for (int i = 1; i <= al; i++) { t += a[i] + b[i]; a[i] = t % 10; t /= 10; } if (t) a[++al] = 1; } int main() { int n; cin >> n; b[++bl] = 1; for (int i = 1; i <= n; i++) { add(a, al, a, al); add(a, al, b, bl); } // 最后再乘以2 add(a, al, a, al); for (int i = al; i; i--) cout << a[i]; return 0; } ```