【经典汉诺塔问题】
汉诺(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 }
【新汉诺塔问题】
给定初始局面和目标局面,求从初始局面到目标局面至少需要多少步。
分析:
- 参考局面:待移动盘子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 }