重温汉诺塔:
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; } }
网友评论