题目描述: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盘. 则是正确的.
解题方法:这道题想了很久,自己并没有很好的方法,最后看了别人的题解,下面是根据别人题解来描述这道题目的解法
记左边的柱子为A,中间的为B,右边的为C。
1)有n个盘子汉诺塔的最少的移动步数是2^n-1;
2)最大的盘子一定不可能出现在中间的柱子上(反之,出现则一定错误)
3)当所有的盘子在左边或者在右边这个序列一定合法。
4)最大的盘子不在中间的柱子上,而初始值最大盘子是在左边的柱子上,则必有:
1、最大的盘子在左边:最大的盘子在左边,由于盘子是从小到大,则意味着要将最大盘子上面的所有盘子移到中间的柱子上。
那么所需要的操作是:A->B 移动N-1个盘子,A->C移动最大的盘子。
可以明显的看见A->B移动N-1盘子是移动最大的盘子的先决条件,也就是前状态。
而这个前状态可以视为有由A->C移动N-2个盘子,再A->B移动N-1中最大的盘子,再将N-2个盘子做操作C->A;
这样子,一个递推规律就产生了。
只要在开始A->C移动最大盘子的时候虚拟的交换B跟C的位置就可以视下一步操作依旧是A->C移动最大的盘子。
2、最大的盘子在右边:最大的盘子在右边,则N-1个盘子一定在中间的柱子上,那么根据上边的规律我们只要在每一次移动的时候交换A,B的位置就可以了
关于汉诺塔问题非递归做法,里面有个lg(m)就操作...
http://placespecial.wordpress.com/2007/10/29/%E6%B1%89%E8%AF%BA%E5%A1%94%E7%9A%84%E4%B8%A4%E7%A7%8D%E9%9D%9E%E9%80%92%E5%BD%92%E8%A7%A3%E6%B3%95%EF%BC%88%E8%BD%AC%EF%BC%89/这是根据别人思路写的一个代码,感触良多。
1#include <iostream>
2#include <cstdio>
3#include <cmath>
4#include <cstring>
5
6using namespace std;
7
8int maxx[4],n[4],h[4][65];
9int a,b,c,i,N,T,temp;
10
11bool working()
12{ while(1)
13 { if(n[c]==N||n[a]==N)
14 {return true; break;}
15 if(n[b]>0&&h[b][maxx[b]]==N)
16 {return false; break;}
17 if(n[a]>0&&h[a][maxx[a]]==N)
18 { N--; n[a]--; maxx[a]++;
19 temp=b;b=c;c=temp;
20 continue;
21 }
22 if(h[c][maxx[c]]==N&&n[c]>0)
23 { N--; n[c]--; maxx[c]++;
24 temp=b;b=a;a=temp;
25 continue;
26 }
27 }
28}
29
30
31int main()
32{
33 scanf("%d",&T);
34 while(T--)
35 { a=1;b=2;c=3;
36 maxx[a]=1;
37 maxx[b]=1;
38 maxx[c]=1;
39 scanf("%d",&N);
40 scanf("%d",&n[a]);
41 for(i=1;i<=n[a];i++)
42 scanf("%d",&h[a][i]);
43 scanf("%d",&n[b]);
44 for(i=1;i<=n[b];i++)
45 scanf("%d",&h[b][i]);
46 scanf("%d",&n[c]);
47 for(i=1;i<=n[c];i++)
48 scanf("%d",&h[c][i]);
49 if(working()) printf("true\n");
50 else printf("false\n");
51 }
52 return 0;
53}
54