ACdream 1219 The Towers of Hanoi Revisited多柱汉诺塔【递归】

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 <disc-radius> from <source-peg> to <target-peg>

if the disc is moved to the empty peg or

move <disc-radius> from <source-peg> to <target-peg> atop <target-top-disc-radius>

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

首先推荐一篇博客:http://www.cnblogs.com/fanzhidongyzby/archive/2012/07/28/2613173.html一点代码没有,但是讲解了原理

再说这个题,我一直纠结于vis【】bool型数组的作用,大概猜到是防止刚刚挪过来的一个(堆)原封不动的再挪回去了。

但是可以遇到某一个顶部半径比想挪的起点小的就马上挪,挪完就return啊

这个题还告诉我们一个道理:预先打表不一定是最好的方法,这个题的数据acdream比zoj的强,按着网上有些程序的init来算,连60 60都输出不了==

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 1e2+5;
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;
        }
    }
}
//void init()
//{
//    int i,j,k;
//    for(i=1; i<=70; i++) //最少三根柱子,才可以开始移动,从这里开始记录数据
//    {
//        f[i][3]=2*f[i-1][3]+1;
//        p[i][3]=i-1;
//    }
//    for(i=4; i<=65; i++) //柱子
//    {
//        f[1][i]=1;
//        for(j=2; j<65; j++)  //盘子
//        {
//            int t=INF;
//            for(k=1; k<j; k++) //先移走k个盘子到一个中间柱子,剩下j-k盘子移动到目标
//            {
//                if(t>f[j-k][i-1]+2*f[k][i])
//                {
//                    t=f[j-k][i-1]+2*f[k][i];
//                    p[j][i]=k;
//                }
//            }
//            f[j][i]=t;
//        }
//    }
//}
int n, m;
int hanoi[MAXN][MAXN], num[MAXN];
int vis[MAXN];
void move(int s,int t)
{
        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]--];
}
void print(int s, int t, int a, int b)
{
    if(a == 1)
    {
        move(s,t);
        return;
    }
    for(int i=1; i<=m; i++)
    {
        if(i!=s && i!=t&&!vis[i])
        {
           // if(hanoi[i][num[i]] > hanoi[s][num[s]-p[a][b]+1])
            {
                print(s, i, p[a][b], b);
                vis[i]=1;
                print(s, t, a-p[a][b], b-1);
                vis[i]=0;
                print(i, t, p[a][b], b);
                return;
            }
        }
    }
    return ;
}
int main()
{

       // for(int i=3;i<=10;i++)for(int j=3;j<=10;j++)printf("i=%d,j=%d,f=%d\n",i,j,f[i][j]);
       //init();
    while(cin>>n>>m)
    {
        memset(f, -1, sizeof(f));
        for(int i=1; i<=65; i++)
        f[1][i] = 1;
        get(n,m);
       // init();
        cout<<f[n][m]<<endl;
        memset(hanoi, 0, sizeof(hanoi));
        memset(num, 0, sizeof(num));
        memset(vis,0,sizeof(vis));
        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;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值