梵塔问题求解及动态显示

首先,什么是梵塔问题?
梵塔问题—有三个杆标号分别为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,实在是不想体验枯燥的生活~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值