【汉诺塔问题】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 }
原文地址:https://www.cnblogs.com/LLGemini/p/4302327.html