汉诺塔II
Gardon是个怕麻烦的人(恩,就是爱偷懒的人),很显然将64个圆盘逐一搬动直到所有的盘子都到达第三个柱子上很困难,所以Gardon决定作个小弊,他又找来了一根一模一样的柱子,通过这个柱子来更快的把所有的盘子移到第三个柱子上。下面的问题就是:当Gardon在一次游戏中使用了N个盘子时,他需要多少次移动才能把他们都移到第三个柱子上?很显然,在没有第四个柱子时,问题的解是2^N-1,但现在有了这个柱子的帮助,又该是多少呢?
1 3 12
1 5 81
<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再增多时就不是当初想的那样了。