美文网首页
汉诺塔的回顾和深刻 汉诺塔VII

汉诺塔的回顾和深刻 汉诺塔VII

作者: 碧影江白 | 来源:发表于2016-07-25 18:12 被阅读554次

重温汉诺塔:

n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列。由于发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各柱子从下往上的大小仍保持如下关系 :

n=m+p+q

a1>a2>...>am

b1>b2>...>bp

c1>c2>...>cq

ai是A柱上的盘的盘号系列,bi是B柱上的盘的盘号系列, ci是C柱上的盘的盘号系列,最初目标是将A柱上的n个盘子移到C盘. 给出1个系列,判断它是否是在正确的移动中产生的系列.

例1:n=3

3

2

1

是正确的

例2:n=3

3

1

2

是不正确的。

注:对于例2如果目标是将A柱上的n个盘子移到B盘. 则是正确的.

Input

包含多组数据,首先输入T,表示有T组数据.每组数据4行,第1行N是盘子的数目N<=64.

后3行如下

m a1 a2 ...am

p b1 b2 ...bp

q c1 c2 ...cq

N=m+p+q,0<=m<=N,0<=p<=N,0<=q<=N,

Output

            对于每组数据,判断它是否是在正确的移动中产生的系列.正确输出true,否则false

Sample Input

6
3
1 3
1 2
1 1
3
1 3
1 1
1 2
6
3 6 5 4
1 1
2 3 2
6
3 6 5 4
2 3 2
1 1
3
1 3
1 2
1 1
20
2 20 17
2 19 18
16 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

Sample Output

true
false
false
false
true
true

这道题让我很好地重温了汉诺塔的计算法则以及递归算法的技巧。

首先需要了解得是汉诺塔的运算思想。个人认为递归的一个最大的特色,或者说优点就是可以不知道算法实现的具体流程和步骤,只要清楚它的思想就可以得到简练正确的代码实现。

言归正传,汉诺塔的实现只需要遵循三步不变的循环步骤,在没有达到预想情况时便一直不停递归。

目的是将a上所有的盘子全部借助b移到c上,并且每次只移动一个,小的只能发在大的上面,设一共有n个盘子。这三个步骤为

1、将最大盘子第n个盘子上的n-1个盘子移到b上。

2、这时,a上只有一个盘子n,则一次就可以把第n个盘子移到c上。

3、将b上的第n-1个盘子移到c上。

由于1,3,步骤中需要移动n-1个盘子,且移动过程遵循以上的法则,那么步骤1:将n-1个盘子借助c从a移动到b上,则又是一个如上的递归,直到成了将一个盘子从n移动到m上借助k,则需要结束循环即可。因此代码表示为:

 以前学过的汉诺塔移动方式的算法是:

void hanno(int n,char a,char b,char c)
{
    if(n)
    {
        hanno(n-1,a,c,b);
        printf("%c->%c\n",a,c);
        hanno(n-1,b,a,c);
    }
}

而这道题目实则也是这三个步骤的考察。

首先,如果没有出现差错,则需要满足以上的移动规则。或者说是现在的三个杆子状态可以满足按照如上的步骤进行移动。

即满足的状态满足下列条件:

1、为了实现将第n个盘子从a(起始位置)移动到c(目标位置),所以应该满足第n个盘子一定不在b(中转位置)上,如果是在中转位置上,则说明还需要将第n个盘子从b移动到c,和上述的转移状态不相符,这样做便是增加了错误移动,次数将增加,所以首先判断最大的是否在b上,如果在,则说明一定不是最优,直接输出false结束。

(如果满足第一个条件,就可以判定了吗?当然不是,除此之外,还需要判定剩余的n-1个盘子仍旧满足条件。由于移动到第n-1个盘子时,起始位置和目标位置都会发生变化,所以也应该将a,b,c进行适当交换使满足b为中转位置。具体步骤为2,3。)

2、如果第n个盘子在a上,下面进行的便是将n-1个盘子从a到b上,需判断第n-1个盘子是不是在c上,可以将从,c,b交换,依旧判断b上是不是有第n-1个盘子。如果有,需要将它移动到目标位置,增加错误移动。

3、如果在c上,则说明上述的三个步骤已经进行了两个,需要进行的操作则是将第n-1个盘子从b移动到c,如果想要顺利移动,第n-1个盘子应该不在a上,此时,将a和b互换,判断目前最大是否在b上。

综上所述,整个过程所做的操作一直都是判断目前最大的盘子是否在b上,每判断一次便把b调整为中转位置。

由题意,每个盘子都所属一个杆子,每个杆子最多可以放的盘子数量也可以预知,所以可以使用标记法,也可使用结构体的方法。

以下是引用的网上大神的代码:

代码如下:

#include <iostream>
using namespace std;

int main()
{
    int s;
    int n;
    int i,j;
    int a,b,c;
    int t[3][64];
    int num[3];
    int cas;

    cin>>cas;
    while(cas--)
    {
        cin>>n;
        a=0;b=1;c=2;
        for(i=0;i<3;i++)
            for(j=0;j<n;j++)
                t[i][j]=0;

        for(i=0;i<3;i++)
        {
            cin>>num[i];
            for(j=0;j<num[i];j++)
            {
                cin>>s;
                t[i][s-1]=1;
            }
        }

        while(n--)
        {
            if(t[b][n])
            {
                cout<<"false"<<endl;
                break;
            }
            if(t[a][n])
            {
                i=b;
                b=c;
                c=i;
            }
            else if(t[c][n])
            {
                i=a;
                a=b;
                b=i;
            }
        }
        if(n==-1)
            cout<<"true"<<endl;
    }
}

相关文章

网友评论

      本文标题:汉诺塔的回顾和深刻 汉诺塔VII

      本文链接:https://www.haomeiwen.com/subject/ncagjttx.html