首先,什么是梵塔问题?
梵塔问题—有三个杆标号分别为A、B、C,初始时在A杆从上到下,按从小到大的顺序放了n个圆盘。需要在有限步数下将n个圆盘平移到C,在平移的过程中,A、B、C杆上的圆盘自上到下都要服从圆盘大小自小到大。
那么怎样求解梵塔问题呢?–问题规约
对于梵塔问题,可以采用问题规约的思路将复杂的问题变成n个小问题的与或图形式。
对于二梵塔问题,也是最简单的问题。先将1号盘移到B,2号盘移到C,1号盘移到C。通过这三个简单的问题就实现了二梵塔问题。
同样对三梵塔,我们可以看成二梵塔和一个圆盘的组合。也就是先将1、2号盘从A移送到B,将3号盘移送到C,再将1、2号盘从B移送到C。而二梵塔问题很好解决。
那么对于n梵塔问题,就转化为了先将1···n-1号盘从A转移到B,将n号盘从A转移到C,再将1···n-1号盘从B转移到C。而将1~n-1号盘从A转移到B以及从B转移到C都是一个n-1梵塔问题。不断将问题进行规约,就变成了单个圆盘的与或图形式。
四梵塔求解如下:
定义下图状态分别表示为(1111)、(1233),那么最终状态可以表示为(3333)
问题规约图如下:
好了,到这里我们已经直到如何求解了,但是怎么进行编程实现呢?
编程实现
由上述的思考过程,对于梵塔的问题可以使用递归算法:将n梵塔从A->C问题变成n-1梵塔A->B、n号从A->C以及n-1梵塔B->C的问题。而每个n-1梵塔问题变成n-2梵塔问题和n-1号圆盘。由此不断进行归约。
当归约到1梵塔,直接输出,否则就需要将问题归约到1梵塔问题。从这个求解思路可以写出递归算法
void fanta(int n, char a, char b, char c)
{
if (n == 1)
{
cout << a << "->" << c << endl;
}
else
{
fanta(n - 1, a, c, b);
cout << a << "->" << c << endl;
fanta(n - 1, b, a, c);
}
}
n代表的是n梵塔问题
这样显示是不是有点枯燥?
旦总说让我做出动态显示,否则就让我和他体验非洲有钱人枯燥的生活
为了不枯燥,定义三个栈M1、M2、M3分别表示A、B、C三个柱圆盘存储情况
定义队列FIN用于存储每一步的M1、M2、M3的情况
初始化栈M1,为里面存储值用于表示不同圆盘的大小,由于后进先出,所以栈顶元素小,初始化过程如下:
for (int i = 1; i <= n; i++)
{
M1.push(n-i+1);
}
FIN.push(M1);
FIN.push(M2);
FIN.push(M3);
当每进行一次圆盘的转移,根据源和目的地进行出栈入栈:
void go(char a,char c)
{
if (a == 'A' && c == 'B')
{
M2.push(M1.top());
M1.pop();
}
else if (a == 'A' && c == 'C')
{
M3.push(M1.top());
M1.pop();
}
if (a == 'B' && c == 'A')
{
M1.push(M2.top());
M2.pop();
}
if (a == 'B' && c == 'C')
{
M3.push(M2.top());
M2.pop();
}
if (a == 'C' && c == 'A')
{
M1.push(M3.top());
M3.pop();
}
if (a == 'C' && c == 'B')
{
M2.push(M3.top());
M3.pop();
}
}
然后需要继续更新FIN:
FIN.push(M1);
FIN.push(M2);
FIN.push(M3);
当所有过程结束后,将FIN依次出队,然后进行绘图:
void draw()
{
int size = FIN.size();
initgraph(900, 600);
int den = 580;//基底
int width = 10;//每一层的高度
int jiange = 20;//层层之间的间隔
for (int i = 0; i < size; i += 3)
{
stack<int>M11(FIN.front());
FIN.pop();
stack<int>M22(FIN.front());
FIN.pop();
stack<int>M33(FIN.front());
FIN.pop();
int size1 = M11.size();
int size2 = M22.size();
int size3 = M33.size();
for (int i = 1; i <= size1; i++)
{
int length = M11.top();
M11.pop();
solidrectangle(150 - 5 * length, den - (size1 - i) * (width + jiange) - width,
150 + 5 * length, den - (size1 - i) * (width + jiange));
}
for (int i = 1; i <= size2; i++)
{
int length = M22.top();
M22.pop();
solidrectangle(450 - 5 * length, den - (size2 - i) * (width + jiange) - width,
450 + 5 * length, den - (size2 - i) * (width + jiange));
}
for (int i = 1; i <= size3; i++)
{
int length = M33.top();
M33.pop();
solidrectangle(750 - 5 * length, den - (size3 - i) * (width + jiange) - width,
750 + 5 * length, den - (size3 - i) * (width + jiange));
}
Sleep(2000);
cleardevice();
}
}
给大家看一下结果吧
动态显示
如果对大家有用,完整文件可以参考我的gitee(github又被墙了不能上传文件)
https://gitee.com/pengpengjy/vatican_question/blob/master/fanta.cpp文件链接
非常期待各位的star,实在是不想体验枯燥的生活~~~