迷宫问题

迷宫问题很经典,对于学习搜索很有帮助,特别是针对深搜,下面我们就来看关于迷宫中的一些常见的问题:

  1. 从起点到终点的最小步数(前提是起点和终点是可达的)
  2. 打印出起点到终点的所有通路。
  3. 打印起点到终点的一条最短路径
1.先来看第一个问题:从起点到终点的最小步数,迷宫中可能起点到终点存在很多的通路(我们找最小的步数,需要把所有的通路都搜遍,才能确定),而我们只用找最小的那条通路的步数就可以了(我们可以想象下,现在给你K个数,叫你去找最小的那个数,你是怎么找的,相信学过编程的你一定不会陌生),为简单起见,我预设的迷宫的起点为左上方,终点为右下方,下面附上具体代码:
#include<iostream>
using namespace std;
//迷宫的维数
#define MAXN 5
int dir[4][2] = {{1,0},{0,-1},{-1,0},{0,1}};
int maze[MAXN][MAXN];
int step_min = 10000000;
void dfs(int depth,int x,int y)
{
    //0代表可行,1代表不可行
    if(x>=0&&x<MAXN&&y>=0&&y<MAXN&&maze[x][y]==0)
    {
        if(x==y&&y==MAXN-1)
        {
            step_min =  step_min > depth ? depth : step_min ;
        }
        maze[x][y] = 1;
        for(int i=0;i!=4;++i)
        {
            dfs(depth+1,x+dir[i][0],y+dir[i][1]);
        }
        maze[x][y] = 0;
    }
}
int main()
{
    for(int i=0;i!=MAXN;++i)
    {
        for(int j=0;j!=MAXN;++j)
        {
            cin >> maze[i][j] ;
        }
    }
    dfs(0,0,0);
    if(step_min==10000000)
    {
        cout << "不可行" << endl;
    }
    else
    {
        cout << "所需的最小步数是:" << step_min << endl;
    }
}
对于搜索最短路径,广搜搜出来的就是一个最短路径,还不用向我们这样,要把所有的通路的都搜出来,做个比较,广搜为什么可以?因为广搜是同步推进的,正如你在河里面扔一个石头,肯定会激起一系列的同心圆,而这些同心圆一遇到岸边,那么就不会继续推进了(抽象的有点。。。。),下面给出用广搜搜索最短路径的代码:
#include<iostream>
#include<queue>
#include<utility>
using namespace std;
#define MAXN 5
#define INF -10000
//由于迷宫里面一个点关联到行和列,此处用了pair来存放数据
//save数组里面保存的是起点到搜索过的点的最小步数
typedef pair<int,int> PAIR;
int maze[MAXN][MAXN],save[MAXN][MAXN];
int dir[4][2] = {{1,0},{0,-1},{-1,0},{0,1}};
int bfs()
{
    queue<PAIR> q_ue;
    for(int i=0;i!=MAXN;++i)
    {
        for(int j=0;j!=MAXN;++j)
        {
            save[i][j] = INF;
        }
    }
    q_ue.push(PAIR(0,0));
    save[0][0] = 0;
    while(!q_ue.empty())
    {
        PAIR p = q_ue.front();
        q_ue.pop();
        if(p.first==p.second&&p.second==MAXN-1)
        {
            break;
        }
        for(int i=0;i!=4;++i)
        {
            int n_x = p.first+dir[i][0],n_y = p.second+dir[i][1];
            if(n_x>=0&&n_x<MAXN&&n_y>=0&&n_y<MAXN&&save[n_x][n_y]==INF&&maze[n_x][n_y]==0)
            {
                //当前步可走,入队
                q_ue.push(PAIR(n_x,n_y));
                //更新最小步数
                save[n_x][n_y] = save[p.first][p.second] + 1 ;
            }
        }
    }
    return save[MAXN-1][MAXN-1] ;
}
int main()
{
    for(int i=0;i!=MAXN;++i)
    {
        for(int j=0;j!=MAXN;++j)
        {
            cin >> maze[i][j] ;
        }
    }
    int flag = bfs();
    if(flag==INF)
    {
        cout << "不可行" << endl;
    }
    else
    {
        cout << "最小步数是:" << flag << endl;
    }
    return 0;
}


2.打印出起点到终点的所有通路.
要打印所有的通路,那么,当我们从起点搜索到一条通路的时候,我们就得把它打印出来,而我们从哪里去打印这条通路,这就需要我们事先把搜索到的这条通路保存起来,下面附上具体代码:
#include<iostream>
using namespace std;
//迷宫的维数
#define MAXN 5
int dir[4][2] = {{1,0},{0,-1},{-1,0},{0,1}};
int maze[MAXN][MAXN];
int path[MAXN*MAXN*2];
void dfs(int depth,int x,int y)
{
    //0代表可行,1代表不可行
    if(x>=0&&x<MAXN&&y>=0&&y<MAXN&&maze[x][y]==0)
    {
        path[2*depth] = x,path[2*depth+1] = y;
        if(x==y&&y==MAXN-1)
        {
            //如果找到一条通路,那么就将其打印出来
            for(int i=0;i!=2*(depth+1);i+=2)
            {
                cout << "(" << path[i] << "," << path[i+1] << ")" << endl;
            }
            cout << endl;
        }
        maze[x][y] = 1;
        for(int i=0;i!=4;++i)
        {
            dfs(depth+1,x+dir[i][0],y+dir[i][1]);
        }
        maze[x][y] = 0;
    }
}
int main()
{
    for(int i=0;i!=MAXN;++i)
    {
        for(int j=0;j!=MAXN;++j)
        {
            cin >> maze[i][j] ;
        }
    }
    dfs(0,0,0);
    return 0;
}
3.打印起点到终点的一条最短路径.
其实,这个问题,可以看成是上面两个小问题的一个综合而已,无非有点特殊的就是,我们要打印最短路径,而path里面保存的只是当前的一条通路(如果存在的话),而我们并非知道这条路径是否是最终要打印的路径,所以我们需要另外定义一个数组来保存path数组里面的值,具体细节,请看代码:
#include<iostream>
using namespace std;
#define MAXN 5
int maze[MAXN][MAXN];
int path[MAXN*MAXN*2],path1[MAXN*MAXN*2];
int dir[4][2] = {{1,0},{0,-1},{-1,0},{0,1}};
int step_min = 10000;
void dfs(int depth,int x,int y)
{
    if(x==y&&y==MAXN-1)
    {
        if(depth < step_min)
        {
            step_min = depth ;
            for(int i=0;i!=2*depth;++i)
            {
                path1[i] = path[i];
            }
        }
        return ;
    }
    for(int i=0;i!=4;++i)
    {
        int n_x = x + dir[i][0],n_y = y + dir[i][1] ;
        if(n_x>=0&&n_x<MAXN&&n_y>=0&&n_y<MAXN&&maze[n_x][n_y]==0)
        {
            maze[n_x][n_y] = 1;
            path[2*depth]=n_x,path[2*depth+1] = n_y ;
            dfs(depth+1,n_x,n_y);
            maze[n_x][n_y] = 0;
        }
    }
}
int main()
{
    for(int i=0;i!=MAXN;++i)
    {
        for(int j=0;j!=MAXN;++j)
        {
            cin >> maze[i][j];
        }
    }
    path[0] = 0,path[1] = 0;
    dfs(1,0,0);
    if(step_min==10000)
    {
        cout << "不可行" << endl;
    }
    else
    {
        for(int i=0;i!=2*step_min;i+=2)
        {
            cout << "(" << path1[i] << ", " << path1[i+1] << ")" << endl;
        }
    }
    return 0;
}
其实,打印最短的一条路径,更好的做法是采用广搜,因为广搜搜出来的就是一条最短路径,而深搜必须要把可走的路径都走了,才知道哪条路径是最短的,所以从效率上看,在这种情况下,广搜明显更具有优势,下面附上具体代码:
#include<iostream>
#include<queue>
#include<utility>
#include<ctime>
#include<cstdlib>
using namespace std;
#define M 10
#define INF -1
int dis[M][M],path[M][M];
//搜索方向
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int maze[M][M];
//点的类型
typedef pair<int,int> P_INT;
P_INT s,e;
//递归打印路径
void print(P_INT t)
{
    if(t==s)
    {
        cout << "(" << t.first << "," << t.second << ")\t";
        return ;
    }
    int i = path[t.first][t.second];
    print(P_INT(t.first-dir[i][0],t.second-dir[i][1]));
    cout << "(" << t.first << "," << t.second << ")\t";
}
//广度优先搜索
void bfs()
{
    queue<P_INT> q;
    //初始化
    for(int i=0;i!=M;++i)
    {
        for(int j=0;j!=M;++j)
        {
            dis[i][j] = INF;
        }
    }
    q.push(s);
    dis[s.first][s.second] = 0;
    while(!q.empty())
    {
        P_INT temp = q.front();
        q.pop();
        if(temp==e)
        {
            break;
        }
        for(int i=0;i!=4;++i)
        {
            int new_x=temp.first+dir[i][0],new_y=temp.second+dir[i][1];
            if(new_x>=0&&new_x<M&&new_y>=0&&new_y<M&&maze[new_x][new_y]==0&&dis[new_x][new_y]==INF)
            {
                q.push(P_INT(new_x,new_y));
                dis[new_x][new_y] = dis[temp.first][temp.second]+1;
                path[new_x][new_y] = i;
            }
        }
    }
    if(dis[e.first][e.second]!=INF)
    {
        print(e);
        cout << dis[e.first][e.second] << endl;
    }
    else
    {
        cout << "invalid." << endl;
    }
}
int main()
{
    srand((unsigned)time(NULL));
    //随机生成迷宫,0代表可走,1代表是墙
    for(int i=0;i!=M;++i)
    {
        for(int j=0;j!=M;++j)
        {
            maze[i][j] = rand()%2;
            cout << maze[i][j] << "  " ;
        }
        cout << endl;
    }
    //输入起点
    cin >> s.first >> s.second ;
    //输入终点
    cin >> e.first >> e.second ;
    bfs();
    return 0;
}


对于深搜问题,我们大部分还是把它看成是树的一种形态,不过,有些路有相应的限制条件。看了上面的,只是对深搜一个初步的了解,如果你还感兴趣的话,可以到poj上面去做一些关于深搜的题目,那样,对你理解深搜会更有帮助。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值