hdu 1207 汉诺塔II

来源:互联网 发布:数据库设计经典案例 编辑:程序博客网 时间:2024/04/30 16:17

汉诺塔II

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4881    Accepted Submission(s): 2390


Problem Description
经典的汉诺塔问题经常作为一个递归的经典例题存在。可能有人并不知道汉诺塔问题的典故。汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往上按大小顺序摞着64片黄金圆盘。上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一回只能移动一个圆盘。有预言说,这件事完成时宇宙会在一瞬间闪电式毁灭。也有人相信婆罗门至今仍在一刻不停地搬动着圆盘。恩,当然这个传说并不可信,如今汉诺塔更多的是作为一个玩具存在。Gardon就收到了一个汉诺塔玩具作为生日礼物。
  Gardon是个怕麻烦的人(恩,就是爱偷懒的人),很显然将64个圆盘逐一搬动直到所有的盘子都到达第三个柱子上很困难,所以Gardon决定作个小弊,他又找来了一根一模一样的柱子,通过这个柱子来更快的把所有的盘子移到第三个柱子上。下面的问题就是:当Gardon在一次游戏中使用了N个盘子时,他需要多少次移动才能把他们都移到第三个柱子上?很显然,在没有第四个柱子时,问题的解是2^N-1,但现在有了这个柱子的帮助,又该是多少呢?
 

Input
包含多组数据,每个数据一行,是盘子的数目N(1<=N<=64)。
 

Output
对于每组数据,输出一个数,到达目标需要的最少的移动数。
 

Sample Input
1312
 

Sample Output
1581
 


四柱汉诺塔问题:

<span style="font-family:FangSong_GB2312;font-size:24px;"><strong>#include<cstdio>  #include<cmath>  int p(int n)  {      int ans,r;    if(n==1) return 1;      ans=0;      r=(int)(sqrt(8*n+1)-1)/2;      ans+=pow(2,r)-1;      ans+=2*p(n-r);      return ans;  }  int main()  {      int n;      while(scanf("%d",&n)==1)          printf("%d\n",p(n));      return 0;  }  </strong></span>


总结:

1. 三柱汉诺塔

三柱汉诺塔是经典的汉诺塔问题,在算法设计中是递归算法的典型问题。

算法: 首先把A 柱上面的n- 1 个碟子通过C 柱移到B 柱上(T(n-1)步),然后把A 柱剩下的一个碟子移到C 柱上(1步), 最后把B 柱上所有的碟子通过A 柱移到C 柱上(T(n-1)步)。

算法的递归方程:T(n)=2*T(n-1)+1。因此,不难算出步数是T(n)=2^n-1。


2. 四柱汉诺塔

首先我们会想到,三柱汉诺塔需要借助另一个柱子存放前n-1个盘子,再把第n个盘子移动到目的位置。

顺其自然的,四柱汉诺塔由于多了一个柱子,我们可以多留下一个盘子n-2,而不让它借位到其他柱子直接移动到目的位置。


算法的基本流程:

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

(2)将第n-2个盘子移动到C上。 (盘子从上到下编号0,1,2,3,4......,n-2,n-1)

(3)将第n-1个盘子移动到D上。

(4)将第n-2个盘子移动到D上。

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

按照以上设计的算法流程,我们得到递归方程:F(n)=2*F(n-2)+3。

因此得到移动步数为:F(n)=4*2^(n/2)-3:n为奇数;F(n)=6*2^(n/2-1)-3:n为偶数。

下边列出6个盘子的移动步数:

n      1     2     3     4     5     6

F(n)  1     3     5     9     13    21

基于此我们甚至可以推广到M(M≥3)个柱子的情况,假设柱子编号为1,2,3…M算法主题框架流程应该如下:

(1)从1柱借助3…M柱子将n-(M-2)个盘子移动到2柱上。

(2)将M-2个通过3…M-1柱简单的移动到M柱上【2*(M-2)-1步骤】。

(3)从2柱借助1,3…M-1柱子将n-(M-2)个盘子移动到M柱上。

这种算法虽然正确,却不是最优!!!

比如:

对于6个盘子4个柱子的汉诺塔,按照我们的想法是保留2个盘子进行移动。现在假如我们保留3个盘子,因此上边的三个盘子按照4柱汉诺塔规则移动到B,步数应该是5,剩下三个盘子按照3柱汉诺塔规则移动到D上,步数应该是2^3-1=7步,然后B上的三个盘子移动到D上仍然是5步,总步数为5+7+5=17步<21步!


从上边的例子中,我们得到一个启示:在递归程序中剩余盘子的个数并不一定是M-2,也有可能是M-1。

假设剩余盘子是M-r,那么r到底取得多少才合适呢?

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)。


对于四柱汉诺塔,当r = (sqrt(8*n+1)-1)/2时,能保证f(n)取得最小值F(n)=(n-(r^2-r+2)/2)*2^r+1。

从这这个方程中也可以看出,在n<6的时候,我们可以验证是和我们起初的构想的结构是相同的,但是当n再增多时就不是当初想的那样了。


0 0