汉诺塔III问题
Description
约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到右边的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面。
现在我们改变游戏的玩法,不允许直接从最左(右)边移到最右(左)边(每次移动一定是移到中间杆或从中间移出),也不允许大盘放到下盘的上面。
Daisy已经做过原来的汉诺塔问题和汉诺塔II,但碰到这个问题时,她想了很久都不能解决,现在请你帮助她。现在有N个圆盘,她至少多少次移动才能把这些圆盘从最左边移到最右边?
Input
包含多组数据,每次输入一个N值(1<=N=35)。
Output
对于每组数据,输出移动最小的次数。
Sample Input
1
3
12
Sample Output
2
26
531440
递归,容易理解,但会超时
如果只有一个盘子,从X到Y再到Z,要两步
如果有n个盘子
先把n-1个盘子从X借助Y移动到Z,再把最大的盘子移动到Y
之后把n-1个盘子从Z借助Y移动到X,再把最大的盘子移动到Z
此时问题规模减小,变成了把n-1个盘子从X借助Y移动到Z
code
#include <stdio.h>
int count=0;
int hanota(int n,char x,char y,char z);
int main()
{
int n;
while(scanf("%d",&n)!=0){
count=hanota(n,'x','Y','Z');
printf("%d\n",count);
count=0;//对计数器清零,方便下次使用
}
}
int hanota(int n,char x,char y,char z)
{
if(n==1){
count+=2;//从X到Y到Z,两步
}
else{
hanota(n-1,'X','Y','Z');//把n-1个盘子从X借助Y移到Z
count++;
hanota(n-1,'Z','Y','X');//把n-1个盘子从Z借助Y移到X
count++;
hanota(n-1,'X','Y','Z');
}
return count;
}
string='''但是超时就很难受啊,本来递归就想了一阵,提交一秒不到就是Time Limit Exceeded,就很伤(fan)心(zao)啊'''
print(string)
普通算法(有大坑)
当用递归测试了几组数据之后,会发现其满足递推式an=3^n-1;
下面我们来推导一番
设汉诺塔函数为F(n)
有n个盘子,我们要把(n-1)个盘子先移到Z,共F(n-1)步,把大盘子移到Y,又是一步,接着把(n-1)个盘子移到X,有F(n-1)步,把大盘子移到Z,又是一步,最后把重新把(n-1)个盘子移到Z,又是F(n-1)步。
不难看出F(n)=F(n-1)*3+2,结合初始条件(n=1时)就可以得出
an=3^n-1
正确代码(等下举几个错误例子)
#include<stdio.h>
#include<math.h>
int main()
{
int n,i;
long long int step=1;
while(scanf("%d",&n)!=EOF){
long long int step=1;
for(i=1;i<=n;i++)
step*=3;
step-=1;
printf("%lld\n",step);
}
}
注意
for(i=1;i<=n;i++)
step*=3;
step-=1;
这段代码在这里不能写成
step=pow(3,n)-1;
因为pow()精度的问题,当数字很大时会出错
还有一个大坑
printf("%lld\n",step);
不能写成
printf("%d",step);
为什么会这样,一开始我也不知道,不管怎么改动都是Wrong Answer,之后发现了这个问题
这个坑也不是我一句两句能说清楚的
详情请参考%I64d 和%lld 的区别传送门1
或者传送门2