迷宫会和

作者: LetUsGoOn | 来源:发表于2019-05-09 11:59 被阅读9次

01.问题描述

涂抹横向和纵向排列的n×*n *个方格中的几个,制作成迷宫。这里规定被涂抹的方格是墙壁,没有被涂抹的就是道路。

两人分别从A点和B 点同时出发,每次前进1 个方格,且按照“右手法则”前进。所谓右手法则,就是靠着右边墙壁前进,不一定要按最短路径前进,但最终要回到入口或者到达出口(其中一人从A 点出发向B 点前进,另一人从B点出发向A 点前进。A 点和B点的位置固定,假设就是左上角和右下角)。求当n=5时,两人在途中相遇的情况一共有多少种?同时到达同一点算相遇,其中一人到达终点,另一人同时回到这个位置也算。

举个例子,当n = 4 时,①的情况下两人可以相遇,②的情况下两人就不能相遇(①的情况下,如果A 像“↓↓↑→→↓↓←→→”这样移动,而B 像“←↑↑→←↑↓←←↑”这样移动,在第5 次移动时两人就会相遇)。

n=4时的示意图

02.算法分析与设计

1)算法分析

解决问题的关键在于“怎样实现用右手法则进行搜索”。首先用0 表示道路,用1 表示墙壁,通过设置0 和1 表示迷宫。那么问题就变成了求有多少种设置方法能使按照右手法则在迷宫中前进的两人相遇。为实现右手法则搜索,要设置一个按右、上、左、下排列,表示移动方向的数组,并且改变这个数组的索引。譬如目前的移动方向是“上”,那么接下来就要按照右、上、左的顺序来依次搜索;如果目前的移动方向是“左”,那么接下来就要按照上、左、下的顺序来搜索,即按逆时针方向进行搜索。

逆时针搜索

当n = 4 时,上述处理几乎可以瞬间完成,但本题里n = 5,所以就要稍微多花费一些时间了。这里可以通过在搜索前判断迷宫是否有效(能否到达目标)的方式优化。如果搜索前就进行判断,那么只需要搜索225= 33554432 种情况中的1225194 种即可,只占全部情况的约3.6%。不过,判断迷宫是否有效也需要一定时间。

2)算法设计

利用5x5的二维数组表示迷宫,用0表示迷宫可通、用1表示不可通的墙壁,则总共有225种情况,对每一种情况进行判断,判断迷宫是否有效,即能否从左上角走到右下角(初始时左上角A的方向向下,右下角B的方向向上),若迷宫有效再按右手法则进行搜索,看A、B是否可以相遇,若A、B相遇则计数加一,若不能相遇则判断下一种情况,直到全部的225种情况判断完成为止。算法主要流程图如下:

算法流程图

关键函数设计

本算法的关键函数主要有三个,分别为exchange()、enable()和search()。其中第一个函数将整数的位转换为迷宫数组,第二个函数判断迷宫能否从A点走到B点(左上角走到右下角),第三个函数在迷宫有效的前提下判断按右手法则搜索A、B能否相遇。

exchange()函数利用简单的位运算或者取余法(用栈作辅助)很容易就实现了。

enable()函数首先判断是否有左上角或者右下角的值为1,若有则应该直接返回false,因为这种情况无论如何也不能从A走到B。剩下的情况利用经典的走迷宫算法进行判断,迷宫入口设置为左上角,出口为右下角。若能走到右下角则返回true,当辅助栈为空时还没有碰到右下角坐标则说明当前迷宫无效,返回false。

search()函数在迷宫有效的前提下判断按右手法则搜索,A与B能否相遇,若相遇返回true,否则返回false。这是本算法最核心的函数,为了便于更好的展示该函数的思想和步骤,画了如下的流程图:

search()函数流程图

03.源代码

#include<stdio.h>
#define N (5)
#include<stack>
using namespace std;
bool mark[N][N];


typedef struct Pos{
    //位置结构体
    int i, j;
    void set(int i, int j)
    {
        this->i = i;
        this->j = j;
    }
}POS;

bool IsPos(const int maze[][N], int i, int j)
{//判断迷宫位置是否合法及是否已经走过
    if (i >= 0 && i < N && j >= 0 && j < N &&maze[i][j] == 0 &&mark[i][j] == true)
        return true;
    else
        return false;
}


bool enable(const int maze[][N])
{//判断迷宫能否从左上角走到右下角
    if(maze[0][0] == 1 || maze[N-1][N-1] == 1) return false;//左上角和右下角为1则迷宫一定不可通
    stack<POS> S;
    
    for(int i=0;i<N;++i)//初始标记数组
    {
        for(int j=0;j<N;++j)
            mark[i][j] = true;
    }
    
    mark[0][0] = false;//设置左上角已经走过
    POS ps; ps.set(0, 0);
    S.push(ps);

    int pos[4][2] = { -1, 0, 0, 1, 1, 0, 0, -1 };
    
    while (!S.empty())
    {
        ps = S.top();
        bool is = false;
        for (int k = 0; k < 4; ++k)
        {
            if (IsPos(maze, ps.i + pos[k][0], ps.j + pos[k][1]))//依次判断迷宫4个方向的走位
            {
                ps.i += pos[k][0]; ps.j += pos[k][1];
                if(ps.i == N-1 && ps.j ==N-1)//如果已经走到右下角,返回true
                    return true;
                    
                mark[ps.i][ps.j] = false;
                S.push(ps);
                is = true;
                break;
            }
        }
        if (!is)
        {
            S.pop();
        }
            
    }
    return false;//没有走通,返回false
}

void show(int maze[][N])
{//打印迷宫方便调试
    for(int i=0;i<N;++i)
    {
        for(int j=0;j<N;++j)
            printf("%d ", maze[i][j]);
        putchar('\n');
    }
}

void exchange(int maze[][N], int n)
{//将n转换成数组保存的迷宫,0为路,1为墙
    for(int i=N-1;i>=0;--i)
    {
        for(int j=N-1;j>=0;--j)
        {
            maze[i][j] = n&1;
            n>>=1;
        }
            
    }


/*取余法将n转换为数组保存的迷宫
    stack<int> S;
    
    while(n)
    {
        S.push(n % 2);
        n /= 2;
    }
    
    while(S.size()< N*N)
        S.push(0);
    
    int cnt=0;
    int i,j;
    i=j=0;
    
    while(!S.empty())
    {
        maze[i][j] = S.top();
        j++;
        ++cnt;
        if(cnt == N)
        {
            j=0;++i;
            cnt = 0;
        }
        S.pop();
    }
    */
}

bool IsPos2(const int maze[][N], int i, int j)
{//判断位置i、j迷宫是否可通
    if (i >= 0 && i < N && j >= 0 && j < N &&maze[i][j] == 0)//判断该位置是否可通
        return true;
    else
        return false;
}

bool search( int maze[][N])
{
    //判断按右手法则搜索A、B能否相遇,相遇返回true否则返回false
    int pos[4][2] = { 0, 1, -1, 0, 0, -1, 1, 0 };
                        
    POS ps1,ps2;
    
    ps1.set(0,0);//初始化A位置
    ps2.set(N-1,N-1);//初始化B位置
    
    int d1,d2;//d1、d2表示方向,0表示向右,1向上,2向左,3向下
    
    d1=3;d2=1;//初始化方向,其中d1为A的方向,d2为B的方向,初始时A向下,B向上
    
    while(1)
    {
        do{ 
            if(IsPos2(maze, ps1.i+pos[d1][0],ps1.j+pos[d1][1]))//按当前方向A是否可以走到下一个位置
            {
                ps1.i+=pos[d1][0];ps1.j+=pos[d1][1];//按当前方向A可以走到下一个位置,更新位置
                d1 = (d1-1+4)%4;//更新方向为当前方向的右手边方向
                break;//A走了一步,跳出循环
            }
            else//按当前方向A不可以走到下一个位置(有墙或者出了迷宫)
                d1 = (d1+1+4)%4;//按逆时针更新方向
        }while(1);
        
        do{//同理对B进行上述操作
            if(IsPos2(maze, ps2.i+pos[d2][0],ps2.j+pos[d2][1]))
            {
                ps2.i+=pos[d2][0];ps2.j+=pos[d2][1];
                d2 = (d2-1+4)%4;
                break;
            }
            else
                d2 = (d2+1+4)%4;
        }while(1);
        
        if(ps1.i == ps2.i && ps1.j == ps2.j)//A、B相遇,返回1
            return 1;
        
        if((ps1.i == N -1 && ps1.j ==N-1) || (ps2.i == 0 && ps2.j==0) )//A、B有一个先到达终点,不可能相遇,返回0
            return 0;
    }
}
int count()
{//计数函数,返回A、B相遇的迷宫模式数量
    int maze[N][N];
    int mazeI;
    int cnt = 0;
    for(mazeI=0; mazeI< (1<<N*N); ++mazeI)//对所有可能的迷宫进行检验(总共2^(N*N)个迷宫)
    {
        exchange(maze, mazeI);  
            if(enable(maze))
            {
                if(search(maze))
                    ++cnt;      
            }
    }
    return cnt;
}
int main()
{
    printf("当n=%d时,一共有%d种满足条件的迷宫。\n",N,count());
}//运行成功 N==5时 一共有660148种满足条件的迷宫 2019年5月1日3:02:11

04.测试结果

测试结果

05.参考文献

[1] 增井敏克[日]. 《程序员的算法趣题》,人民邮电出版社,2017年;

相关文章

  • 迷宫会和

    01.问题描述 涂抹横向和纵向排列的n×*n *个方格中的几个,制作成迷宫。这里规定被涂抹的方格是墙壁,没有被涂抹...

  • 画迷宫

    今天,我在美术课上,老师给我们拿了一个迷宫兔子和几本迷宫书,我们看完迷宫书,老师说:“今天的主题是画迷宫”...

  • wind爸爸:5分钟设计迷宫游戏

    今天我们开始聊如何和孩子一起自己设计更好玩的迷宫游戏。 自己设计迷宫游戏我把它分为纸上迷宫、DIY迷宫两大类,下面...

  • 创建型模式---抽象工厂模式

    例子--迷宫假定我们现在来实现一个迷宫,迷宫由一个个房间组成,而房间之间由墙和 门来连接。所以迷宫就有几个基础组件...

  • 萌宝日记236之迷宫游戏

    2019年1月22日 星期二 晴 我们今天晚上的游戏是迷宫。我们的迷宫游戏和通常认为的迷宫游戏不太一样,我和妈妈用...

  • 你试着用左手写过字吗

    女儿三岁,给她买了连线书,迷宫书来玩。过去,我一直以外,连线和迷宫,是因为孩子对走迷宫的天然好奇,最近才知道,这和...

  • 【狐狸爸爸故事屋】迷宫历险记

    狐狸姐姐最近迷上了迷宫游戏。 这天,狐狸姐姐和小熊、小猴,准备自己组建一个迷宫。 他们来到草地上,开始制作迷宫。 ...

  • C++迷宫算法,跟着大牛学,通俗易懂,快速上手

    VC++ SDK 迷宫算法,另有火焰和飘雪代码,生成迷宫地图,并不包括迷宫游戏的所有功能,仅为SDK初级开发者提供...

  • 妹妹会走迷宫啦

    今晚,我在书房折腾自己的事儿,只听妹妹喊道:“妈妈,这是我送你的花哦!” 我低头一看,妹妹捧着迷宫...

  • 你的人生还是个死胡同吗?

    人生和迷宫 迷宫,小时候你喜欢玩吗?多半是喜欢的。不知道前面是什么,充满了期待和幻想。捉迷藏可以说是迷宫的变形,小...

网友评论

    本文标题:迷宫会和

    本文链接:https://www.haomeiwen.com/subject/cpxwoqtx.html