基于C++的递归法解决汉诺塔问题(很详细)


先看问题


1-问题来源

布拉马教堂的牧师们碰到了一个难题。有一个金制的平板,上面有3根钻石针,其中一根针上有按大小排好顺序的64个金盘子。这些牧师每天移动一个盘子,并遵守原顺序把这些盘子移到另一根针上的时候,时间将会终结。问:如果牧师们每天移动一个盘子,而他们在0年的时候开始,那么什么时候时间终结?


2-问题提炼:

如下图所示,遵循以下三个规则将盘子从左边的柱子移到右边的柱子上:

(1)一个盘子拿走之后,它必须放回某个柱子上;

(2)一次只能移动一个盘子,而且必须是柱子最上面的那个;

(3)大盘子不能放在小盘子上面。

在这里插入图片描述


3-问题分析

从确定一个初始状态开始,这种情况非常简单:如果只有一个盘子,那么从A柱移到C柱即可。当有n个盘子时,为了将n个盘子从A柱移到C柱,必须把最大的盘子n从A柱移动到C柱上,所以首先要把其上面的n-1个盘子搬到B柱上,然后在A柱上的第n个盘子直接搬到C柱上可以直接解决。剩下的问题是把n-1个盘子从B柱移动到C柱,于是求解n阶汉诺塔问题变成求解n-1阶汉诺塔问题,问题降了一阶,可以看出这是一个递归问题。递归终止条件为n=1,遵循降阶的步骤,经过有限步后一定能达到n=1,这样就可以使用递归函数求解此问题了。
按照前面的分析,将n个盘子从A柱移到C柱可以分解为下面3个步骤:

(1)移动A柱最上面的n-1个盘子到B柱,使用C柱中转;

(2)把A柱上剩下的一个盘子移到C柱上;

(3)将n–1个盘子从B柱移到C柱上,使用A柱中转;

这三个步骤包含两种操作:

(1)将多个盘子从一个柱移到另一个柱上,这是一个递归的过程。

(2)将1个盘子从一个柱上移到另一柱上。


代码示例

//汉诺塔游戏
#include <iostream>

using namespace std;
int count=0;
void move(char getone,char putone)
{
    
    	 
	cout<<getone<<"-->"<<putone<<endl;
	count++;
} 
 
 void hanot(int n,char A,char B,char C)
 {
    
    
 	if(n==1)
		move(A,C);
 	else
 	{
    
    
 		hanot(n-1,A,C,B);	//将第n-1个盘从A移动到B 
 		move(A,C);			//将第n个盘从A移动到C 
 		hanot(n-1,B,A,C);	//将第n-1个盘从B移动到C 
	 }
 }
 
 int main()
 {
    
    
 	while(1)
	{
    
    
		int m;
	 	cout<<"输入盘子数:";
		cin>>m;
		cout<<"移动"<<m<<"个盘的步骤为:"<<endl;
		hanot(m,'A','B','C');
		cout<<"总共移动的步数为"<<count<<"次"<<endl;
		cout<<"------------------------------"<<endl;
	}	
	return 0; 
 }

其运行效果如下

在这里插入图片描述
程序的主要部分是
在这里插入图片描述
不妨取n=2代入,就可以很容易的理解程序了
在这里插入图片描述

但是当我把n取3或者更大时,程序所执行的步骤就会变得很大,递归开始散发出它的巨大魔力,成功地把我弄得头晕转。在草稿纸上写程序执行步骤写到放弃的我,突然闹光一闪,决定在把程序执行的步骤都打印出来,如下:

在这里插入图片描述
其代码如下:

//汉诺塔游戏
#include <iostream>
#include <windows.h>
#include <string.h>
using namespace std;

//设置在控制台打印的字体的颜色,可选white,red,green,blue,yellow,magenta,cyan 
void textcolor(char *color)
{
    
    
	if(!strcmp(color,"white")) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
	else if(!strcmp(color,"red")) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY | FOREGROUND_RED);
	else if(!strcmp(color,"green")) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY | FOREGROUND_GREEN);
	else if(!strcmp(color,"blue")) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY | FOREGROUND_BLUE);
	else if(!strcmp(color,"yellow")) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
	else if(!strcmp(color,"magenta")) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);
	else if(!strcmp(color,"cyan")) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
}

void move(char getone,char putone)
{
    
    	 
	textcolor("red");
	cout<<getone<<"-->"<<putone<<endl;		//在控制台打印出移动步骤为红色 
	textcolor("cyan");
 } 
 
 void hanot(int n,char A,char B,char C)
 {
    
    
 	cout<<"if("<<n<<"==1)"<<endl;
 	if(n==1)
 	{
    
    
 		cout<<"move("<<A<<","<<C<<");"<<endl;
		move(A,C);
	}
 	else
 	{
    
    
 		cout<<"hanot("<<n-1<<","<<A<<","<<C<<","<<B<<");"<<endl;
 		hanot(n-1,A,C,B);	//将第n-1个盘从A移动到B 
 		cout<<"move("<<A<<","<<C<<");"<<endl;
 		move(A,C);			//将第n个盘从A移动到C 
 		cout<<"hanot("<<n-1<<","<<B<<","<<A<<","<<C<<");"<<endl;
 		hanot(n-1,B,A,C);	//将第n-1个盘从B移动到C 
	 }
 }
 
 int main()
 {
    
    
 	while(1)
	{
    
    
		int m;
		textcolor("white");
	 	cout<<"输入盘子数:";
		cin>>m;
		cout<<"移动"<<m<<"个盘的步骤为红色,程序的执行步骤为青色:"<<endl;
		textcolor("cyan");
		hanot(m,'A','B','C');
		cout<<"------------------------------"<<endl;
	}	
	return 0; 
 }

再看看函数的主要部分,程序执行步骤是不是就很清晰了?
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_46977029/article/details/109252971