汉诺塔游戏
时间限制: 1 s|空间限制: 32000 KB
题目描述 Description
汉诺塔问题(又称为河内塔问题),是一个大家熟知的问题。在A,B,C三根柱子上,
有n个不同大小的圆盘(假设半径分别为1-n吧),一开始他们都叠在我A上(如图所示),
你的目标是在最少的合法移动步数内将所有盘子从A塔移动到C塔。
游戏中的每一步规则如下:
1. 每一步只允许移动一个盘子(从一根柱子最上方到另一个柱子的最上方)
2. 移动的过程中,你必须保证大的盘子不能在小的盘子上方
(小的可以放在大的上面,最大盘子下面不能有任何其他大小的盘子)
如对于n=3的情况,一个合法的移动序列式:
1 from A to C
2 from A to B
1 from C to B
3 from A to C
1 from B to A
2 from B to C
1 from A to C
给出一个数n,求出最少步数的移动序列
输入描述 Input Description
一个整数n
输出描述 Output Description
第一行一个整数k,代表是最少的移动步数。
接下来k行,每行一句话,N from X to Y,表示把N号盘从X柱移动到Y柱。X,Y属于{A,B,C}
样例输入 Sample Input
3
样例输出 Sample Output
7
1 from A to C
2 from A to B
1 from C to B
3 from A to C
1 from B to A
2 from B to C
1 from A to C
数据范围及提示 Data Size & Hint
n<=10
思路分析:
我们设定三个柱子A,B,C。我们的目的是将环从A–>C。(A为起始位置,C为目标位置)
当N=1即一阶时它的路径很简单只需要从A->C进行移动。
当N=2时我们需要进行三步:
1.小盘 A->B
(假想没有大盘只有小盘,与N=1 的步骤一样,只是目标位置变为了 B)
2.大盘 A->C
(大盘上面的小盘到B去了,与N=1 的步骤一样直接到C )
3.小盘 B->C
(大盘到了C,对于小盘而言,C可以看作无盘,与N=1 的步骤一样,只是起始位置变为 B )
(分解一下,小盘从A通过B作为中间目标再到C。可以这样想
小盘下面的大盘目标是C 所以小盘第一次目标则变成B,
等到大盘到了目标C ,小盘再到C。
则完成将大小盘按小盘在上大盘在下的要求移到C。)
当N=3时我们需要进行七步:
1. 小盘 A->C 2.中盘 A->B 3.小盘 C->B
(假想没有大盘只有小盘和中盘,与N=2 的步骤一样,只是目标位置变为了 B)
4. 大盘 A->C,
(大盘上面的小盘和中盘都到B去了,与N=1 的步骤一样直接到C )
5. 小盘 B->A 6.中盘 B->C 7.小盘 A->C
(大盘到了C,对于小盘和中盘而言,C可以看作无盘,与N=2 的步骤一样,只是起始位置变为了 B )
(分解一下,大盘想从A去C。但上面压着小盘与中盘 ,
所以得先把他们移开 并且上面两盘不能移动到C,得移动到B 去
就相当于N=2时,起始位置A到目标位置B。待大盘移动到C。
当前在B 的小盘和中盘,完全就是执行N=2 的步骤。从当前起始位置B 到目标位置C.)
如此执行,通过递归方式。代码思路如下:
1. 对于执行最大盘(n) 到C的操作之前,肯定是?把次大盘(n-1)从A移动到 B
2. 执行最大盘(n) 到C的操作
3.对于执行最大盘(n) 到C的操作之后,肯定是?把次大盘(n-1)从B移动到C
每次只关心上一层,上上层是到了上一层才考虑的事------递归
题目链接:http://codevs.cn/problem/3145/
#include <stdio.h>
void han(int n, char A, char B, char C){
if(n == 1)printf("%d from %c to %c\n", n, A, C);
else{
//第一步 对于执行最大盘(n) 到C的操作之前
han(n-1, A, C, B);
//第二步 执行最大盘(n) 到C的操作
printf("%d from %c to %c\n", n, A, C);
//第三步 对于执行最大盘(n) 到C的操作之后
han(n-1, B, A, C);
}
}
int main()
{
int n;
scanf("%d", &n);
printf("%d\n", (1 << n) - 1);
han(n, 'A', 'B', 'C');
return 0;
}
非递归 方法:
1,2,...,n 表示n 个盘子.数字大盘子就大.n 个盘子放在第A号柱子上.大
盘不能放在小盘上.在第A根柱子上的盘子是a[1],a[2],...,a[n].
a[1]=n,a[2]=n-1,...,a[n]=1.即a[1]是最下面的盘子.把n 个盘子
移动到第C 号柱子.每次只能移动1 个盘子,且大盘不能放在小盘上.
问: 第m 次移动的是哪一个盘子,从哪根柱子移到哪根柱子.
例如:n=3,m=2.
回 答是 :2 A-->B,即移动的是2 号盘,从第A根柱子移动到第C 根柱子 。
A号柱有n 个盘子,叫做源柱.移往C 号柱,叫做目的柱.B 号柱叫做中间柱.
全部移往C 号柱要f(n) =(2^n)- 1 次.
最大盘n 号盘在整个移动过程中只移动一次,n-1 号移动2 次,i 号盘移动
2^(n-i)次.
1 号盘移动次数最多,每2 次移动一次.
第2k+1 次移动的是1 号盘,且是第k+1 次移动1 号盘.
第4k+2 次移动的是2 号盘,且是第k+1 次移动2 号盘.
第(2^s)k+2^(s-1)次移动的是s 号盘,这时s 号盘已被移动了k+1 次.
每2^s 次就有一次是移动s 号盘.
第一次移动s 号盘是在第2^(s-1)次.
第二次移动s 号盘是在第2^s+2^(s-1)次.
第k+1 次移动s 号盘是在第k*2^s+2^(s-1)次.
A-->B-->C-->A 叫做顺时针方向,A-->C-->B-->A叫做逆时针方向.
最大盘n 号盘只移动一次:A-->C,它是逆时针移动.
n-1 移动2 次:A-->B-->C,是顺时针移动.
如果n 和k 奇偶性相同,则k 号盘按逆时针移动,否则顺时针.
代码:
#include<stdio.h>
int main(void){
long long i, res;
int n, l;
long long m, j;
long long s, t;
scanf("%d", &n);
res=(1<<n)-1;
printf("%lld\n",res);
for( i=1; i <=res; i++ ){
s = 1; t = 2;
for( l=1; l <= n; l++ ){
if( i%t == s ) break;
s = t; t *= 2;
}
j = i/t;
if( n%2 == l%2 ){// 逆时针
if( (j+1)%3 == 0 ) printf("%d from B to A\n",l);
if( (j+1)%3 == 1 ) printf("%d from A to C\n",l);
if( (j+1)%3 == 2 ) printf("%d from C to B\n",l);
}
else{// 顺时针
if( (j+1)%3 == 0 ) printf("%d from C to A\n",l);
if( (j+1)%3 == 1 ) printf("%d from A to B\n",l);
if( (j+1)%3 == 2 ) printf("%d from B to C\n",l);
}
}
return 0;
}