四柱加强版汉诺塔HanoiTower----是甜蜜还是烦恼

我想很多人第一次学习递归的时候,老师或者书本上可能会举汉诺塔的例子。

但是今天,我们讨论的重点不是简单的汉诺塔算法,而是三柱汉诺塔的延伸。先来看看经典的三柱汉诺塔。

 

一、三柱汉诺塔(Hanoi_Three):

我想大家对于三柱汉诺塔的理解以及算法的实现应该是很熟练了。

我在这里简单的过一遍三柱汉诺塔的算法思想:

有A、B、C三根柱子,A柱上有n个盘子,现在需要将A上所有的盘子转移到C上,请给出搬运次数最少的步骤。

 

算法思想:

1、将A上n-1个盘子以C为缓存,全部转移到 B 柱上。

2、将A上留下的第n个盘子,直接转移到 C  柱上。

3、将B上的n-1个盘子,以A为缓存,全部转移到 C 柱上。

 

很容易得到算法的递归方程为:T(n)=2*T(n-1)+1,不难算出步数是T(n)=2^n-1

 

具体的代码如下:

[cpp]  view plain copy
  1. void Move( char x, char y )  
  2. {  
  3.     printf("%c --> %c\n", x, y); //打印路径,x-->y  
  4. }  
  5.   
  6. void Hanoi_Three( int n, char a, char b, char c )  
  7. {  
  8.     if( n <= 0 )  
  9.         return ;      
  10.       
  11.     step++;     //步骤+1  
  12.       
  13.     if( n == 1 )  
  14.     {  
  15.         Move( a, c );   //将a柱上的一个盘子直接移动到c柱  
  16.         return ;      
  17.     }  
  18.     else  
  19.     {  
  20.         Hanoi_Three( n-1, a, c, b);     //将a上n-1个盘子以c为缓存,全部转移到 b 柱上  
  21.         Move( a, c );                   //将A上留下的第n个盘子,直接转移到 C  柱上  
  22.         Hanoi_Three( n-1, b, a, c);     //将b上的n-1个盘子,以a为缓存,全部转移到 c 柱上  
  23.     }  
  24. }  


二、四柱汉诺塔(Hanoi_Four):

 

当柱子为四根时,对于只是将盘子全部转移到另一根柱子上这个目的来说,是大大降低了难度,而且算法的复杂度也大大降低了。但是,这个时候,如果要你找到一个最优的、步骤最少的实现方法,可以说难度是提升了一个数量级。

有些人可能会质疑,为什么,我用三柱汉诺塔的思想不是很优化了吗?

别急,且让我慢慢向你道来。

 

先来看看这种‘看上去很合理’的解法:

假设,A,B,C,D,分别为:源位置,缓存,缓存,目的位置。

因为三柱的时候,我们是将A的前n-1个盘子放到B上缓存,然后将第n个盘子放到C柱上。

现在的情况好很多,有两个可以缓存的柱子,因此,看上去移动起来更加方便,原来B上需要缓存的n-1个盘子,现在可以只是n-2个盘子,而将第n-1个盘子放到C上缓存。

 

具体的流程如下(非最优解法):

1、从A借助C、D将 n-2个盘子移动到B上。

2、将第n-1个盘子移动到C上。

3、将第n个盘子移动到D上。

4、将第n-1个盘子移动到D上。

5、从B借助A、C将 n-2个盘子全部移动到D上。

 

看上去,非常完美,笔者也一度觉得这个思想没有破绽,甚至我还自以为找到了k根柱子汉诺塔的通用方法(想当然的将B柱上缓存的数量从n-2个,改为n-(k-2)个盘子)。直到我看了这篇文章:多柱汉诺塔最优算法设计探究

    虽然我们想到让盘子尽量不发生重叠来保证步数的最少,但是这并不能绝对保证。或许在盘子较少的情况下是可行的,但是盘子增多时,那些多余的只有一个盘子的柱子是可以加以利用的(可能的优化在这里)。虽然这么做加多了每次的移动步数,但是却从另一个侧面减少了递归的数量,因此我们需要从这里边找一个平衡点。

 

下面我们来看看,1941年,美国的J. S. Frame,给出的四柱汉诺塔的算法思想,也叫Frame算法

1、用4柱汉诺塔算法把A柱上部分的n- r个碟子通过C柱和D柱移到B柱上【F( n- r )步】。

2、用3柱汉诺塔经典算法把A柱上剩余的r个碟子通过C柱移到D柱上【2^r-1步】(参照上述三柱时的情况)。

3、用4柱汉诺塔算法把B柱上的n-r个碟子通过A柱和C柱移到D柱上【F(n-r)步】。

4、依据上边规则求出所有r(1≤r≤n)情况下步数f(n),取最小值得最终解。

 

因此Frame算法的递归方程如下:

F(n)=min(2*F(n-r)+2^r-1),(1≤r≤n)。

 

大家有没有发现,其实,这个算法思想跟我们之前认为合理的算法基本一致,差别只是在于他将我们的n-2个碟子缓存到B上,改为了将n- r个碟子转移到B柱上。

差别即使核心,这个算法的核心,就是计算n个盘子的情况下,r为何值时,能够使得算法最优。

 

找到了核心,我们现在的任务就明确了,就是对r值的计算。

这里给出了一个较笨的方法--枚举(不知道各位有没有其他方法)。就是将一定范围内的n与r的所有取值带入,得到满足F(n)为最小值的r的值,记为K[n] = r;

具体的代码如下:

[cpp]  view plain copy
  1. void Init_K(void )  
  2. {  
  3.     int i, k;     
  4.     __int64 temp;     
  5.     __int64 m[Max+1] = {0};       
  6.       
  7.     for( i = 1; i <= Max; i++ )  
  8.     {  
  9.         m[i] = INT_MAX;       
  10.         for( k = 1; k <= i; k++ )  
  11.         {  
  12.             temp = 2*m[i-k] + (__int64)pow(2,k) - 1;      
  13.             if( temp < m[i] )  
  14.             {  
  15.                 m[i] = temp;      
  16.                 K[i] = k;     
  17.                 //printf("K[%d] = %d, m[i] = %d\n", i, k, temp );     
  18.             }  
  19.         }  
  20.     }  
  21. }  


得到各个n对于的r之后,算法将变的非常简单,具体实现如下:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <math.h>  
  3. #define Max 100   
  4. #define INT_MAX 0xfffffffffffffff  
  5. int K[Max+1] = {0};   
  6. int step = 0;     
  7.   
  8. void Hanoi_Four( int n, char a, char b, char c, char d );     
  9. void Hanoi_Three( int n, char a, char b, char c );    
  10. void Move( char x, char y );      
  11.   
  12. void Move( char x, char y )  
  13. {  
  14.     printf("%c --> %c\n", x, y); //打印路径,x-->y  
  15. }  
  16.   
  17. void Hanoi_Three( int n, char a, char b, char c )  
  18. {  
  19.     if( n <= 0 )  
  20.         return ;      
  21.       
  22.     step++;     //步骤+1  
  23.       
  24.     if( n == 1 )  
  25.     {  
  26.         Move( a, c );   //将a柱上的一个盘子直接移动到c柱  
  27.         return ;      
  28.     }  
  29.     else  
  30.     {  
  31.         Hanoi_Three( n-1, a, c, b);     //将a上n-1个盘子以c为缓存,全部转移到 b 柱上  
  32.         Move( a, c );                   //将A上留下的第n个盘子,直接转移到 C  柱上  
  33.         Hanoi_Three( n-1, b, a, c);     //将b上的n-1个盘子,以a为缓存,全部转移到 c 柱上  
  34.     }  
  35. }  
  36.   
  37. void Hanoi_Four( int n, char a, char b, char c, char d )  
  38. {  
  39.     if( n <= 0 )  
  40.         return ;      
  41.       
  42.     if( n == 1 )  
  43.     {  
  44.         step++;   
  45.         Move( a, d );     
  46.         return ;      
  47.     }  
  48.     else  
  49.     {  
  50.         int kn = K[n];    
  51.         //printf("kn = %d\n", K[n]);      
  52.         Hanoi_Four( n-kn, a, c, d, b );     //用4柱汉诺塔算法把A柱上部分的n- kn个碟子通过C柱和D柱移到B柱上  
  53.         Hanoi_Three( kn, a, c, d );         //用3柱汉诺塔经典算法把A柱上剩余的kn个碟子通过C柱移到D柱上。  
  54.         Hanoi_Four( n-kn, b, a, c, d );     //用4柱汉诺塔算法把B柱上的n-r个碟子通过A柱和C柱移到D柱上  
  55.     }  
  56. }  
  57.   
  58. void Init_K(void )  
  59. {  
  60.     int i, k;     
  61.     __int64 temp;     
  62.     __int64 m[Max+1] = {0};       
  63.       
  64.     for( i = 1; i <= Max; i++ )  
  65.     {  
  66.         m[i] = INT_MAX;       
  67.         for( k = 1; k <= i; k++ )  
  68.         {  
  69.             temp = 2*m[i-k] + (__int64)pow(2,k) - 1;      
  70.             if( temp < m[i] )  
  71.             {  
  72.                 m[i] = temp;      
  73.                 K[i] = k;     
  74.                 //printf("K[%d] = %d, m[i] = %d\n", i, k, temp );     
  75.             }  
  76.         }  
  77.     }  
  78. }  
  79.   
  80. int main()  
  81. {  
  82.     int n;    
  83.     Init_K();     
  84.   
  85.     printf("Please enter the number of the Plates: \n");  
  86.     while( scanf("%d", &n) != EOF )  
  87.     {  
  88.         step = 0;     
  89.         Hanoi_Four( n, 'A''B''C''D' );      
  90.         //Hanoi_Three( n, 'A', 'B', 'C' );    
  91.         printf("**************************\nTotal Step: %d\n", step );    
  92.   
  93.         printf("Please enter the number of the Plates: \n");  
  94.     }  
  95.       
  96.     return 0;     
  97. }  

 

到这里,四柱汉诺塔的算法基本讲完了。

有兴趣的同学,可以继续归纳多柱汉诺塔的实现方法,欢迎交流指导!

 

 

 很多时候,看似合理的背后,其实是一种思维定势。。。

from: http://blog.csdn.net/cyh_24/article/details/8075578

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值