• 奇怪的汉诺塔


    Hanoi塔问题的原型

    问题描述:现在有n个圆盘从上往下从小到大叠在第一根柱子上,要把这些圆盘全部移动到第三根柱子要怎么移动呢?请找出需要步骤数最少的方案

    转载自https://blog.csdn.net/liujian20150808/article/details/50793101

    =====================转载开始=====================

    首先,我们从简单的例子开始分析,然后再总结出一般规律。

    当n = 1的时候,即此时只有一个盘子,那么直接将其移动至C即可。移动过程就是 A -> C

    当n = 2的时候,这时候有两个盘子,那么在一开始移动的时候,我们需要借助B柱作为过渡的柱子,即将A柱最上面的那个小圆盘移至B柱,然后将A柱底下的圆盘移至C柱,最后将B柱的圆盘移至C柱即可。那么完整移动过程就是A -> B , A -> C , B -> C

    当n = 3的时候,那么此时从上到下依次摆放着从小到大的三个圆盘,根据题目的限制条件:在小圆盘上不能放大圆盘,而且把圆盘从A柱移至C柱后,C柱圆盘的摆放情况和刚开始A柱的是一模一样的。所以呢,我们每次移至C柱的圆盘(移至C柱后不再移到其他柱子上去),必须是从大到小的,即一开始的时候,我们应该想办法把最大的圆盘移至C柱,然后再想办法将第二大的圆盘移至C柱......然后重复这样的过程,直到所有的圆盘都按照原来A柱摆放的样子移动到了C柱。

    那么根据这样的思路,问题就来了:

    如何才能够将最大的盘子移至C柱呢?

    那么我们从问题入手,要将最大的盘子移至C柱,那么必然要先搬掉A柱上面的n-1个盘子,而C柱一开始的时候是作为目标柱的,所以我们可以用B柱作为"暂存"这n-1个盘子的过渡柱,当把这n-1的盘子移至B柱后,我们就可以把A柱最底下的盘子移至C柱了。

    而接下来的问题是什么呢?

    我们来看看现在各个柱子上盘子的情况,A柱上无盘子,而B柱从上到下依次摆放着从小到大的n-1个盘子,C柱上摆放着最大的那个盘子。

    所以接下来的问题就显而易见了,那就是要把B柱这剩下的n-1个盘子移至C柱,而B柱作为过渡柱,那么我们需要借助A柱,将A柱作为新的"过渡"柱,将这n-1个盘子移至C柱。

    根据上面的分析,我们可以抽象得出这样的结论:

    汉诺塔函数原型:

    1 void Hanio(int n,char start_pos,char tran_pos,char end_pos)

    那么我们把n个盘子从A柱移动至C柱的问题可以表示为:

    Hanio(n, A, B, C);

    那么从上面的分析得出:

    该问题可以分解成以下子问题:

    第一步:将n-1个盘子从A柱移动至B柱(借助C柱为过渡柱)

    第二步:将A柱底下最大的盘子移动至C柱

    第三步:将B柱的n-1个盘子移至C柱(借助A柱为过渡柱)

    因此完整代码如下所示:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int step; //记录步数
     4 void move(int n, char from, char to) { //将编号为n的盘子由from柱子移动到to柱子(目标柱子)
     5     printf("第%d步:将%d号盘子移动:%c柱子---->%c柱子
    ", step++, n, from, to);
     6 }
     7 //汉诺塔递归函数参数解释
     8 //n表示要将多少个"圆盘"从起始柱子移动至目标柱子
     9 //start_pos表示起始柱子, tran_pos表示过渡柱子, end_pos表示目标柱子
    10 void Hanio(int n, char start_pos, char tran_pos, char end_pos) {
    11     if (n == 1) { //递归结束的终点:当n==1时, 只需要直接将圆盘从起始柱子移至目标柱子即可.
    12         move(n, start_pos, end_pos);
    13     } else {
    14         Hanio(n - 1, start_pos, end_pos, tran_pos); //一开始先将n-1个盘子移至过渡柱上
    15         move(n, start_pos, end_pos);                //然后再将底下的大盘子直接移至目标柱子即可
    16         Hanio(n - 1, tran_pos, start_pos, end_pos); //处理放在过渡柱上的n-1个盘子,此时借助原来的起始柱作为过渡柱(因为起始柱已经空了)
    17     }
    18 }
    19 int main() {
    20     int n;
    21     cin >> n;
    22     step = 1; //赋初始值
    23     Hanio(n, 'A', 'B', 'C');
    24     cout << "总步数:" <<  step - 1 << endl;
    25     return 0;
    26 }

    对于n个盘子,移动的总步数为2^n - 1

    =====================转载结束=====================

    然后是稍微进阶版的蓝桥杯的汉诺塔问题

    问题描述
      如果将课本上的Hanoi塔问题稍做修改:仍然是给定N只盘子,3根柱子,但是允许每次最多移动相邻的M只盘子(当然移动盘子的数目也可以小于M),最少需要多少次?
      例如N=5,M=2时,可以分别将最小的2个盘子、中间的2个盘子以及最大的一个盘子分别看作一个整体,这样可以转变为N=3,M=1的情况,共需要移动7次。
    输入格式
      输入数据仅有一行,包括两个数N和M(0<=M<=N<=8)
    输出格式
      仅输出一个数,表示需要移动的最少次数
    样例输入
    5 2

    样例输出

    7
     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int ans; //移动步数
     4 void move(int n, int m, char a, char b, char c) { 
     5     //参数解释:将n个盘子,最多一次拿m个,从a柱子开始,以b柱子为过渡,移动到c柱子上
     6     if (n <= m) { //如果可以一次拿完,直接拿
     7         ans++;
     8     } else { //如果一次拿不完
     9         move(n - m, m, a, c, b); //先把上面的n-m个盘子,从a移动到b,以c为过渡。这样就露出来这m个盘子了
    10         ans++; //将这露出来的m个盘子直接拿过去
    11         move(n - m, m, b, a, c); //将n-m个盘子从b移动到c,以a为过渡
    12     }
    13 }
    14 int main() {
    15     int n, m;
    16     cin >> n >> m;
    17     move(n, m, 'a', 'b', 'c');
    18     cout << ans << endl;
    19     return 0;
    20 }

    然后就是这次的题目了

     多了一个塔,用动态规划

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 15;
     4 int d[N]; //d[n]表示求解n盘3塔的最小步数
     5 int f[N]; //f[n]表示求解n盘4塔问题的最小步数
     6 int main() {
     7     d[1] = 1; //如果只有一个盘子,只需要移动一次
     8     for (int i = 2; i <= N; i++) { //i表示盘子的数量
     9         /*然后接下来这行代码就包含很多内容了
    10         当现在盘子数量为i的时候
    11         可以先把前i - 1个盘子,从a柱移动到b柱上,以c柱为过渡
    12         再把最后一个盘子,从a柱直接移动到c柱
    13         再把i - 1个盘子,从b柱移动到c柱,以a柱为过渡
    14         */
    15         d[i] = 2 * d[i - 1] + 1;
    16     }
    17     memset(f, 0x3f, sizeof(f)); //初始化为正无穷
    18     f[0] = 0; //0个盘子不需要做任何操作
    19     for (int i = 1; i <= N; i++) {
    20         for (int j = 0; j < i; j++) { //枚举一下前j个盘子
    21             f[i] = min(f[i], f[j] * 2 + d[i - j]);
    22         }
    23     }
    24     for (int i = 1; i <= 12; i++) {
    25         cout << f[i] << endl;
    26     }
    27     return 0;
    28 }

    解析:

    设d[n]表示求解n盘3塔问题的最小步数

    递推式:d[n] = 2 * d[n-1] + 1

    即把前n-1个盘子从A柱移到B柱,然后把A柱上剩的那一个盘子移动到C柱,最后把B柱上的那n-1个盘子移动到C柱上

    设f[n]表示求解n盘4塔问题的最小步数

    递推式:f[n] = min{2 * f[i] + d[n - i]}

    初始化:f[1] = 1(一个盘子在4塔模式下移动到D柱需要1步)
    先把i个盘子在4塔模式下移动到B柱,
    然后把n-i个盘子在3塔模式下移动到D柱(因为不能覆盖到B柱上,就等于只剩下A、C、D柱可以用)
    最后把i个盘子在4塔模式下移动到D柱
    考虑所有可能的i取最小值,即得到上述递推公式

  • 相关阅读:
    Flash 9/Flash CS 3 HTTP Post 请求Web Service by .net
    Custom Draw
    [转]NDIS开发[网络驱动开发]
    http header详解
    [MSDN]用 SQL Server 2005中的 CLR 集成
    JSF 页面之间传值
    [转]采用HttpModules来重写URLs(实践篇)
    [转]聚集索引和非聚集索引(sql server索引结构及其使用)
    [转]使用showModalDialog打开模态窗口添加数据后刷新原窗口
    ASP.Net中自定义Http处理及应用之HttpHandler篇
  • 原文地址:https://www.cnblogs.com/fx1998/p/13910305.html
Copyright © 2020-2023  润新知