【汉诺塔问题】UVa 10795 - A Different Task

【经典汉诺塔问题】

  汉诺(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;

 

代码如下:

【汉诺塔问题】UVa 10795 - A Different Task
 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 \n", 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\n", 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\n", ++kase, ans);

38     }

39     return 0;

40 }

 

你可能感兴趣的:(task)