试题 算法提高 汉诺塔
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
汉诺塔是一个古老的数学问题:
有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆:
每次只能移动一个圆盘;
大盘不能叠在小盘上面。
提示:可将圆盘临时置于B杆,也可将从A杆移出的圆盘重新移回A杆,但都必须遵循上述两条规则。
问:如何移?最少要移动多少次?
输入格式
一行,包含2个正整数,一个是N,表示要移动的盘子数;一个是M,表示最少移动步数的第M步
输出格式
共2行。
第一行输出格式为:#No: a->b,表示第M步骤具体移动方法,其中No表示第M步移动的盘子的编号(N个盘子从上到下依次编号为1到n),表示第M步是将No号盘子从a杆移动到b杆(a和b的取值均为{A、B、C})。
第2行输出一个整数,表示最少移动步数。
样例输入
2
样例输出
#2: A->B
7
数据规模与约定
0<N<20,0<M<=最少移动步数
试题解析
下面举例的3种情况如不好理解建议跟着思路自己画图理解!
假设n=1:那么直接需要一步把第1个(即第n个)从A移到C即:#1: A->C共1步
假设n=2:那么需要把第1个(即第n-1个)从A移动到B;在把第2个(即第n个)移动到C;然后把第1个(即第n-1个)从B移动到C即#2: A->B,#1: A->C,#2: B->C共3步
假设n=3:那么需要先把第1个(即第n-2个)从A移动到C然后把第2个(即第n-1个)从A移动到B然后把第1个(即第n-2个)从C移动到B然后把第3个(即第n个)从A移到C然后把第1个(即第n-2个)从B移到A然后把第2个(即第n-1个)从B移到C然后把第1个(即第n-1个)从A移到C即:#1: A->C,#2: A->B,#1: C->B,#3: A->C,#1: B->A,#2: B->C,#1: A->C共7步
总结规律:先假设f(n)为把n个盘子从一根柱子全部移动到另外一根柱子所需要的最少步数。要想实现f(n)需要分三步如下图
要实现f(n-1)有可以按照f(n)的思路分为三个步骤,这就是递归的思路,根据这个可以总结出一个等式f(n+1)=2f(n)+1(n为任意的正整数)即
f(n+1)+1=2(f(n)+1)又f(n)>0所以数列f(n)+1为以2为首项2为公比的等比数列
可以求出f(n)=2n-1
但题目不仅要求最少的步数还要求给出第m步的操作过程,所以我们需要根据上面的分三步思路定义新的函数Hanoi(n,A,B,C),它表示的意思是把在A的n块通过B的辅助移动到C(不通过C在n-1大于2时是无法直接移动到B的)那么根据上面的三步走可以如此递归
要实现Hanoi(n,A,B,C)
先得实现Hanoi(n-1,A,C,B)即把在A的n-1块通过C的辅助移动到B
然后A上只剩下第n块所以把第n个从A移动到C即:#n: A->C
然后实现Hanoi(n-1,B,A,C)即把在B的n-1块通过A的辅助移动到C
在添加一个计步器count在第m步时输出即可
代码
#include<stdio.h>
// n,m对应题意,count是计步器
int n,m,count=0;
/**
* 在只有一块的情况下直接把这一块从A移到C不需要借助B
* 1.要把n个盘子借助B移到C就先要把n-1个在A的盘子借助C移到B
* 2.现在n-1个盘子在B,就把第n个从A移到C
* 3.然后把n-1个在B上的盘子借助A移动到C
*/
void Hanoi(int n,char A,char B,char C){
if(n==1){
count++;
if(count==m){
printf("#%d: %c->%c\n",n,A,C);
}
return;
}
Hanoi(n-1,A,C,B);
count++;
if(count==m){
printf("#%d: %c->%c\n",n,A,C);
}
Hanoi(n-1,B,A,C);
}
int main(){
scanf("%d%d",&n,&m);
/**
* 先要把n个在A上的盘子借助B放到C
*/
Hanoi(n,'A','B','C');
printf("%d",count);
return 0;
}