zoukankan      html  css  js  c++  java
  • 【汉诺塔问题】UVa 10795

    【经典汉诺塔问题】

      汉诺(Hanoi)塔问题:古代有一个梵塔,塔内有三个座A、B、C,A座上有64个盘子,盘子大小不等,大的在下,小的在上。有一个和尚想把这64个盘子从A座移到B座,但每次只能允许移动一个盘子,并且在移动过程中,3个座上的盘子始终保持大盘在下,小盘在上。在移动过程中可以利用B座,要求打印移动的步骤。如果只有一个盘子,则不需要利用B座,直接将盘子从A移动到C。

    • 如果有2个盘子,可以先将盘子1上的盘子2移动到B;将盘子1移动到c;将盘子2移动到c。这说明了:可以借助B将2个盘子从A移动到C,当然,也可以借助C将2个盘子从A移动到B。
    • 如果有3个盘子,那么根据2个盘子的结论,可以借助c将盘子1上的两个盘子从A移动到B;将盘子1从A移动到C,A变成空座;借助A座,将B上的两个盘子移动到C。这说明:可以借助一个空座,将3个盘子从一个座移动到另一个。
    • 如果有4个盘子,那么首先借助空座C,将盘子1上的三个盘子从A移动到B;将盘子1移动到C,A变成空座;借助空座A,将B座上的三个盘子移动到C。

    即:f(n)=f(n-1)+1+f(n-1)=(2^n)-1;  f(1)=1;

    代码如下:

     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 
     5 int step = 0;
     6 void move ( char sour, char dest )
     7 {
     8     printf ( "move from %c to %c 
    ", sour, dest);
     9 }
    10 void hanoi ( int n, char sour, char temp, char dest )
    11 {
    12     if (n == 1)
    13     {
    14         move (sour, dest);
    15         ++step;
    16     }
    17     else
    18     {
    19         hanoi ( n-1, sour, dest, temp );
    20         move (sour,dest);
    21         ++step;
    22         hanoi ( n-1, temp, sour, dest );
    23     }
    24 }
    25 int main ()
    26 {
    27     int n = 4;
    28     hanoi ( n, 'A', 'B', 'C' );
    29     printf ( "Total steps is %d
    ", step );
    30     return 0;
    31 }
    View Code

    【新汉诺塔问题】

      给定初始局面和目标局面,求从初始局面到目标局面至少需要多少步。

    分析:

    • 参考局面:待移动盘子k在其初始柱子上且k上无其他盘子,k的目标柱子为空,中间柱子从上到下依次为1,2,...,k-1盘子。
    • 首先找不在目标柱子上最大的盘子k,因为如果最大的盘子在目标柱子上它不需要移动,也不碍事。
    • 剩下的任务与经典汉诺塔问题类似,即将k-1个盘子移到参考局面;将盘子k移到目标柱子;再将k-1个盘子移到目标局面(因移动是可逆的,故可看做是将目标局面的盘子移到参考局面)。可以看出这是个递归问题。

    即我们需要一个函数f(P, i, final) : 将编号为1~i的盘子全部移到柱子final上所需要的步数(数组P表示各盘子的初始柱子编号,除去盘子的初始柱子x和最终柱子y剩下的那根柱子编号为6-x-y,因为只有柱子1,2,3);

    则:ans = f(start,k-1,6-start[k]-finish[k])+f(finish,k-1,6-start[k]-finish[k])+1;

    至于函数f的计算,当i为0时,无需移动;否则将1~i-1的盘子全部移到参考局面。若i-1号盘子本身在中转柱子上,那么就等同于只移动1~i-2号盘子到final柱子上。移动1~i-1号盘子由参考局面到目标局面的过程即为经典汉诺塔过程,步数为2^(i-1)-1;此处注意答案要用long long;

     代码如下:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 using namespace std;
     5 const int maxn = 65;
     6 int n, A[maxn], B[maxn];
     7 //f(P, i, final) : 将编号为1~i的盘子全部移到柱子final上所需要的步数
     8 //(数组P表示各盘子的初始柱子编号,除去盘子的初始柱子x和最终柱子y剩下的那根柱子编号为6-x-y)
     9 long long f(int* P, int i, int fin)
    10 {
    11     if(!i) return 0;
    12     if(P[i] == fin) return f(P, i-1, fin);
    13     return f(P, i-1, 6-P[i]-fin) + 1 + ((1LL << (i-1)) - 1);
    14 }
    15 int main()
    16 {
    17     int kase = 0;
    18     while(scanf("%d", &n) && n)
    19     {
    20         for(int i = 1; i <= n; i++)
    21         {
    22             scanf("%d", &A[i]);
    23         }
    24         for(int i = 1; i <= n; i++)
    25         {
    26             scanf("%d", &B[i]);
    27         }
    28         int k = n;
    29         while(k && A[k] == B[k])    k--;
    30 
    31         long long ans = 0;
    32         if(k >= 1)
    33         {
    34             int tmp = 6-A[k]-B[k];
    35             ans = f(A, k-1, tmp) + 1 + f(B, k-1, tmp);
    36         }
    37         printf("Case %d: %lld
    ", ++kase, ans);
    38     }
    39     return 0;
    40 }
  • 相关阅读:
    SAP资产变动明细
    SAP资产明细报表
    SAP连接HANA数据库
    工程变更记录报表
    SELECTION-SCREEN 文本丢失
    FG函数模块
    DOI EXCEL显示报表
    OLE填充EXCEL
    下载模板,上传EXCEL
    SELECTION-SCREEN 加按钮
  • 原文地址:https://www.cnblogs.com/LLGemini/p/4302327.html
Copyright © 2011-2022 走看看