Acdream 1219 The Towers of Hanoi Revisited(递归汉诺塔问题)

传送门

The Towers of Hanoi Revisited

Special Judge Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others)

Submit Statistic Next Problem

Problem Description

  You all must know the puzzle named "The Towers of Hanoi". The puzzle has three pegs and N discs of different radii, initially all disks are located on the first peg, ordered by their radii - the largest at the bottom, the smallest at the top. In a turn you may take the topmost disc from any peg and move it to another peg, the only rule says that you may not place the disc atop any smaller disk. The problem is to move all disks to the last peg making the smallest possible number of moves.
  There is the legend that somewhere in Tibet there is a monastery where monks tirelessly move disks from peg to peg solving the puzzle for 64 discs. The legend says that when they finish, the end of the world would come. Since it is well known that to solve the puzzle you need to make 2N - 1 moves, a small calculation shows that the world seems to be a quite safe place for a while.
  However, recent archeologists discoveries have shown that the things can be a bit worse. The manuscript found in Tibet mountains says that the puzzle the monks are solving has not 3 but M pegs. This is the problem, because when increasing the number of pegs, the number of moves needed to move all discs from the first peg to the last one following the rules described, decreases dramatically. Calculate how many moves one needs to move N discs from the first peg to the last one when the puzzle has M pegs and provide the scenario for moving the discs.

Input

  Input file contains N and M (1 ≤ N ≤ 64, 4 ≤ M ≤ 65).

Output

  On the first line output L - the number of moves needed to solve the puzzle. Next L lines must contain the moves themselves. For each move print the line of the form

move from to

if the disc is moved to the empty peg or

move from to atop

if the disc is moved atop some other disc.

Disc radii are integer numbers from 1 to N, pegs are numbered from 1 to M.

Sample Input

5 4

Sample Output

13

move 1 from 1 to 3

move 2 from 1 to 2

move 1 from 3 to 2 atop 2

move 3 from 1 to 4

move 4 from 1 to 3

move 3 from 4 to 3 atop 4

move 5 from 1 to 4

move 3 from 3 to 1

move 4 from 3 to 4 atop 5

move 3 from 1 to 4 atop 4

move 1 from 2 to 1

move 2 from 2 to 4 atop 3

move 1 from 1 to 4 atop 2

题目大意:

就是给你N个盘子,M个柱子,让你求的是怎么样操作使第一个柱子上的全部的盘子移动到最后一个盘子,操作数目最少并将移动的方法写出来。

解题思路:

在这路首先介绍一个算法—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)。

通过这个方程我们能得到所有4柱汉诺塔的步骤个数,同时也有人证明[1]了,对于四柱汉诺塔,当

r=((8?n+1)????????√?1)/2

时,能保证f(n)取得最小值

F(n)=(n?(r2?r+2)/2)?2r+1

。所以算法的复杂度是

F(n)=O((2?n)?????√?2(2?n)√)

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

接下来就是根据四柱的Frame算法来求解M柱的问题

(1)用M柱汉诺塔算法把1柱上部分的n-r个碟子通过3…M柱移到2柱上【M( n- r )步】。

(2)用M-1柱汉诺塔算法把1柱上剩余的r个碟子通过3…M-1柱移到M柱上【(r)步】。

(3)用M柱汉诺塔算法把2柱上的n-r个碟子通过1柱和3…M柱移到M柱上【M( n- r )步】。

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

其实我们的这个问题就是基于这个算法求解的就是一个递归的问题:

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 1e2+5;
const double eps = 1e-9;
const int INF = 1e8+5;
int f[MAXN][MAXN], p[MAXN][MAXN];///f:步数 p:节点
void get(int n, int k)
{
    if(f[n][k] != -1)
        return;
    f[n][k] = INF;
    if(k < 3)
        return;
    for(int m=1; m<n; m++)
    {
        get(m, k);
        get(n-m, k-1);
        int tp = 2*f[m][k]+f[n-m][k-1];
        if(f[n][k] > tp)
        {
            f[n][k] = tp;
            p[n][k] = m;
        }
    }
}
int n, m;
int hanoi[MAXN][MAXN], num[MAXN];
void print(int s, int t, int a, int b)
{
    if(a == 1)
    {
        printf("move %d from %d to %d ",hanoi[s][num[s]]+1,s,t);
        if(num[t])
            printf("atop %d",hanoi[t][num[t]]+1);
        puts("");
        num[t]++;
        hanoi[t][num[t]]=hanoi[s][num[s]--];
        return;
    }
    for(int i=1; i<=m; i++)
    {
        if(i!=s && i!=t)
        {
            if(hanoi[i][num[i]] > hanoi[s][num[s]-p[a][b]+1])
            {
                print(s, i, p[a][b], b);
                print(s, t, a-p[a][b], b-1);
                print(i, t, p[a][b], b);
                return;
            }
        }
    }
    return ;
}
int main()
{
    while(cin>>n>>m)
    {
        memset(f, -1, sizeof(f));
        for(int i=1; i<=m; i++)
            f[1][i] = 1;
        get(n, m);
        cout<<f[n][m]<<endl;
        memset(hanoi, 0, sizeof(hanoi));
        memset(num, 0, sizeof(num));
        for(int i=n; i>=1; i--)
        {
            hanoi[1][num[1]] = i;
            num[1]++;
        }
        for(int i=1; i<=m; i++)
            hanoi[i][0] = INF;
        print(1, m, n, m);
    }
    return 0;
}
时间: 2024-12-27 20:16:58

Acdream 1219 The Towers of Hanoi Revisited(递归汉诺塔问题)的相关文章

URAL 2029 Towers of Hanoi Strike Back 汉诺塔,从初始状态到任意给出状态需要的次数

F - Towers of Hanoi Strike Back Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice URAL 2029 Description The Tower of Hanoi puzzle was invented by French mathematician édouard Lucas in the second half

【数据结构与算法】递归汉诺塔

汉诺塔 汉诺塔是根据一个传说形成的数学问题(关于汉诺塔): 有三根杆子A,B,C.A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小.要求按下列规则将所有圆盘移至C杆: 每次只能移动一个圆盘: 大盘不能叠在小盘上面. 提示:可将圆盘临时置于B杆,也可将从A杆移出的圆盘重新移回A杆,但都必须遵循上述两条规则. 递归汉诺塔 解题思路: 可以把问题简化成2个盘子的情况,如:A上有两个盘子,B和C是空的.如果要把A的两个盘子全部移动到C,需要经过以下步骤: 1.A移动一个盘子到B 2.A移动一个盘

递归--汉诺塔问题 (Hanoi)

汉诺塔问题(Hanoi):古代有一个梵塔,塔内有三个座A.B.C,A座上有64个盘子,盘子大小不等,大的在下,小的在上(如图).有一个和尚想把这64个盘子从A座移到C座,但每次只能允许移动一个盘子,并且在移动过程中,3个座上的盘子始终保持大盘在下,小盘在上.在移动过程中可以利用B座,要求输出移动的步骤 . 结题思路:利用递归思想结题,就是找出其每步的共同规律,然后必须有一步是递归的终止条件. 先假设第一种情况:3根柱,从左到右A,B,C,上面小号,底部大号,从上往下递增. 如要完成该任务,经历3

递归汉诺塔

/*汉诺塔的玩法: * 游戏的规则:将A柱上的盘子移动到C柱上,大盘必须在小盘之上. * 1 当A柱上只有一个盘子的时候,直接移动到C柱上: * 2 当A柱上有两个盘子的时候, *   将A柱上的1盘(从上到下编号)移动到B柱, *   将A柱上的2盘移动到C柱, *   将B柱上的1盘移动到C柱: *   (将A上的1~n-1盘---->B柱,将A柱上n---->C柱,B柱上的1~n-1盘---->C柱) * 3 当A柱上有三个盘子的时候,将A柱上的1~2盘移动到B柱, *   将A柱

2017.11.26 计算机算法之分治与递归——汉诺塔

1.我的递归算法(纯粹的递归) #include <stdio.h>//当盘子数n等于15时,移动次数已经达到32767,运行时间已经达到15.540s long long count; void hanoi(int n,char a,char b,char c)//借助C将A上的盘子全部移动到B { if(n==0) return; hanoi(n-1,a,c,b); printf("%c --> %c\n",a,b); count++; hanoi(n-1,c,b

递归——汉诺塔问题(python实现)

规则 每次移动一个盘子 任何时候大盘子在下面,小盘子在上面 方法 假设共n个盘子 当n=1时: 直接把A上的一个盘子移动到C上(A->C) 当n=2时: 把小盘子从A放到B上(A->B)这里开始采用参数,rsc源地址=A,dst目的地址=B 把大盘子从A放到C上( A->C)rsc=A, dst=C 把小盘子从B放到C上(B->C)rsc=B, dst=C 当n=3时: 把A上的两个盘子,通过C移动到B上去, 调用递归实现(A-C->B)rsc=A, trans中转=C, d

PKU《程序设计》专项课程_递归汉诺塔问题

取自coursera.org上公开课北京大学<C程序设计进阶> 递归调用注意的点 1.关注点放在求解的目标上,递推是,目标放在开头 2.找到第N次和第(N-1)次之间的关系,通项公式 3.给出边界(比如第1次执行结果,斐波那契数列是第1次和第2次结果) #include <iostream> using namespace std; void move(int m,char x, char y,char z) { if(m==1) { cout<<"把一个盘子

[Python]递归汉诺塔

move_count = 0 ; def hanoi(n,src,buffer,dst): 'n:需移动的盘子个数,src:盘子原来的位置,buffer:盘子可临时使用的位置,dst:盘子的目标移动位置' global move_count; if n < 1: print('输入有误'); return None; elif n==1: print(src + '----->' + dst); move_count += 1; else: hanoi(n-1,src,dst,buffer);

递归--汉诺塔

---恢复内容开始--- //n:盘子个数 a,b,c用字符表示三根柱子 void hanoiTower(int n, char a, char b, char c) { static int step = 0; if (n == 1) { cout << ++step<<": "<<a << "→" << c << endl; return; } else { hanoiTower(n - 1