POJ3083-Children of the Candy Corn(DFS+BFS+模拟方向)

重要的事情说10遍!?????????
当你样例全过、自编样例全过,怎么怎么觉得都对,但就是WA的时候,请在提交代码时,换一下编译器,可能就A了。
比如这样子:(手动微笑)
在这里插入图片描述
在这里插入图片描述
交了十几遍WA,心态都炸了!!!!我真的难受啊!!!!!!!!
————————————————我是分割线————————————————
好了好了,言归正传,开始这道比较水的题目的题解。
题目大意
给一个迷宫,S是入口,E是出口,#是墙,.是空地
分别输出左优先步数、右优先步数、最短路步数
左优先就是走到一个位置,按照左、上、右、下的顺序进行移动,右优先则是“右上左下”
其中,题目中还给定了一些限制:
①只有一个入口和一个出口,且规定从入口出发,一定能找到出口。
②为了确保左优先和右优先一定能找到出口,规定入口和出口都在迷宫的四周且不在转角处。(都在边界对于初始化的面向的确定很有帮助,后文会提到。)
③S和E至少要相隔一堵墙。(其实没啥用)

小贴士:
说它水,是因为原理很基础,做题思路很快就能出来。DFS模拟面向和转向,BFS直接求最短路就OK了。
但是,在模拟面向和转向的时候,需要去思考,这篇博客贴的代码是“真·模拟”,我写完之后,看了很多博主的优秀博客,发现他们的代码很简单,因为同样是在模拟面向和转向的时候,他们进行了思考,找到了小规律、小技巧,从而将代码长度缩短,简便程度提高。
不是马后炮,我觉得如果我看完题目,拿着笔动动手去画一画的话,找到这个规律不难。因为我就是凭空去想,只能想到比较裸的“真·模拟”,前期思维过程少,意味着你的代码可能要长很多很多。所以经过这道题,我觉得还是要勤动手,不要懒,如果想到的方法感觉上实现有些麻烦的话,要去寻找一些小规律,动手+动脑是最高效的方法。
————————————————我是分割线————————————————

首先贴一下比较简单的做法的博文吧。
POJ3083-Children of the Candy Corn
接下来就是“真·模拟”的方法。

每进入一次DFS,需要进行两步操作:按照当前面向进行移动改变面向
以左优先代码为例:

void dfs_l(int x,int y,int dir)
{
    ///当前操作
    num[0]++;
    ///结束条件
    if(maze[x][y]=='E'){
        success=true;
        return;
    }
    for(int i=0;i<4;i++){
        ///进行移动
        int nx=x+step_l[dir][i][0];
        int ny=y+step_l[dir][i][1];
        ///深搜条件
        if(nx>=0&&nx<h&&ny>=0&&ny<w&&!success&&maze[nx][ny]!='#'){
            dfs_l(nx,ny,change_l[dir][i]);
        }
    }
}

DFS还是按照四步走的原则:当前操作、结束条件、递归条件、是否回溯
①当前DFS深度需要进行计数操作
②结束条件是“找到出口”
③for循环四个方向,按照当前面向移动、递归条件
④递归时,改变面向并传入下一层DFS
这道题不需要回溯,因为是按照左优先的方式,一直递归下去,如果走到死胡同,需要往回走,但是不需要重新计数,而是在原来的基础上继续计算需要走的步数。故而不需要对已经访问的点进行标记、解除标记。

然后介绍移动数组改变面向数组,还是以左优先为例。
1、移动数组

int step_l[4][4][2]={
    ///x代表四种面向不同的走法,y代表每种面向的按顺序的四种不同走法,z表示横纵坐标变化
                    {{0,-1},{-1,0},{0,1},{1,0}},///上
                    {{0,1},{1,0},{0,-1},{-1,0}},///下
                    {{1,0},{0,-1},{-1,0},{0,1}},///左
                    {{-1,0},{0,1},{1,0},{0,-1}}///右
};

是的,你没看错,我用了一个三维数组来模拟移动数组。
第一维代表,当前面向——0上、1下、2左、3右
第二维代表,当前面向下,按优先级排列的四种走法
第三维代表,横纵坐标的变化
故而你看到的移动代码是这样的:

		int nx=x+step_l[dir][i][0];
        int ny=y+step_l[dir][i][1];

dir代表当前面向,i代表第几种走法,01对应xy。也对应了上述三维数组的定义,模拟了移动。

听上去很麻烦,但是并不难想到,事实上也不难实现。
对比右优先的移动数组,你可以发现,就在左优先的基础上,把四种走法的第1、3号进行了交换而已。即,左右优先进行了交换,因为左优先是“左上右下”的顺序,而右优先是“右上左下”的顺序。

int step_r[4][4][2]={
                    {{0,1},{-1,0},{0,-1},{1,0}},///上
                    {{0,-1},{1,0},{0,1},{-1,0}},///下
                    {{-1,0},{0,-1},{1,0},{0,1}},///左
                    {{1,0},{0,1},{-1,0},{0,-1}}///右
};

2、改变面向数组

///change数组代表不同面向时,四种走法后,面向的改变
int change_l[4][4]={{2,0,3,1},{3,1,2,0},{1,2,0,3},{0,3,1,2}};
int change_r[4][4]={{3,0,2,1},{2,1,3,0},{0,2,1,3},{1,3,0,2}};

又是一个二维数组。
第一维代表,当前面向——0上、1下、2左、3右
第二维代表,当前面向下,按优先级排列的四种走法对应的,要改变成的面向
故而你看到的传参是这样的:

dfs_l(nx,ny,change_l[dir][i]);

dir代表当前面向,i代表第几种走法,对应了上述二维数组的定义,模拟了改变面向。

3、BFS求最短路
然后是求最短路的BFS,很裸,也不用注意什么,直接敲就行了。
这里用到的_move数组就是普通的移动数组,不需要考虑面向,四个方向什么顺序都OK。
有关BFS的细节就不多讲了,不会的可以参考一些优秀的BFS的讲解博客。

struct point{
    int x,y;
    point(int x=0,int y=0):x(x),y(y){}
};
bool vis[maxn][maxn];
int ans[maxn][maxn];
int _move[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int bfs(int x,int y)
{
    queue<point> q;
    q.push(point(x,y));
    vis[x][y]=true;
    while(!q.empty()){
        point v=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            int nx=v.x+_move[i][0];
            int ny=v.y+_move[i][1];
            if(nx>=0&&nx<h&&ny>=0&&ny<w&&maze[nx][ny]!='#'&&!vis[nx][ny]){
                vis[nx][ny]=true;
                q.push(point(nx,ny));
                ans[nx][ny]=ans[v.x][v.y]+1;
                if(maze[nx][ny]=='E') return ans[nx][ny];
            }
        }
    }

}

4、主函数
再就是主函数里DFS第一层传入的面向参数了,由于入口在边界,所以面向很容易确定下来,只需要根据它的行数或列数即可确定。0上、1下、2左、3右。

		if(sx==h-1)
            init_dir=0;
        else if(sx==0)
            init_dir=1;
        else if(sy==w-1)
            init_dir=2;
        else if(sy==0)
            init_dir=3;

还要注意一点,输入是先w后h,即先列后行,不要搞错了。

给俩样例自己测试吧~
5
5 5
##E##
#…#
#…##
#…#
###S#
6 8
####S#
#…#.#
##.#.#
#…#
##…#
E…#.#
#…#

AC代码(要用C++交!要用C++交!要用C++交!要用C++交!要用C++交!)

#include<stdio.h>
#include<queue>
#define maxn 50
using namespace std;
int w,h;
bool success;
char maze[maxn][maxn];
int sx,sy;
int p,q;
int num[3];///分别代表三种走法的步数
int step_l[4][4][2]={
    ///x代表四种面向不同的走法,y代表每种面向的按顺序的四种不同走法,z表示横纵坐标变化
                    {{0,-1},{-1,0},{0,1},{1,0}},///上
                    {{0,1},{1,0},{0,-1},{-1,0}},///下
                    {{1,0},{0,-1},{-1,0},{0,1}},///左
                    {{-1,0},{0,1},{1,0},{0,-1}}///右
};
int step_r[4][4][2]={
                    {{0,1},{-1,0},{0,-1},{1,0}},///上
                    {{0,-1},{1,0},{0,1},{-1,0}},///下
                    {{-1,0},{0,-1},{1,0},{0,1}},///左
                    {{1,0},{0,1},{-1,0},{0,-1}}///右
};
///change数组代表不同面向时,四种走法后,面向的改变
int change_l[4][4]={{2,0,3,1},{3,1,2,0},{1,2,0,3},{0,3,1,2}};
int change_r[4][4]={{3,0,2,1},{2,1,3,0},{0,2,1,3},{1,3,0,2}};


void dfs_l(int x,int y,int dir)
{
    ///当前操作
    num[0]++;
    ///结束条件
    if(maze[x][y]=='E'){
        success=true;
        return;
    }
    for(int i=0;i<4;i++){
        ///进行移动
        int nx=x+step_l[dir][i][0];
        int ny=y+step_l[dir][i][1];
        ///深搜条件
        if(nx>=0&&nx<h&&ny>=0&&ny<w&&!success&&maze[nx][ny]!='#'){
            dfs_l(nx,ny,change_l[dir][i]);
        }
    }
}
void dfs_r(int x,int y,int dir)
{
    ///当前操作
    num[1]++;
    ///结束条件
    if(maze[x][y]=='E'){
        success=true;
        return;
    }
    for(int i=0;i<4;i++){
        ///进行移动
        int nx=x+step_r[dir][i][0];
        int ny=y+step_r[dir][i][1];
        ///深搜条件
        if(nx>=0&&nx<h&&ny>=0&&ny<w&&!success&&maze[nx][ny]!='#'){
            dfs_r(nx,ny,change_r[dir][i]);
        }
    }
}

///以上是dfs,以下是bfs
struct point{
    int x,y;
    point(int x=0,int y=0):x(x),y(y){}
};
bool vis[maxn][maxn];
int ans[maxn][maxn];
int _move[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int bfs(int x,int y)
{
    queue<point> q;
    q.push(point(x,y));
    vis[x][y]=true;
    while(!q.empty()){
        point v=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            int nx=v.x+_move[i][0];
            int ny=v.y+_move[i][1];
            if(nx>=0&&nx<h&&ny>=0&&ny<w&&maze[nx][ny]!='#'&&!vis[nx][ny]){
                vis[nx][ny]=true;
                q.push(point(nx,ny));
                ans[nx][ny]=ans[v.x][v.y]+1;
                if(maze[nx][ny]=='E') return ans[nx][ny];
            }
        }
    }

}
int main()
{
    int n,init_dir;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d%d",&w,&h);
        for(int i=0;i<h;i++)
            scanf("%s",maze[i]);
        for(int i=0;i<h;i++)
            for(int j=0;j<w;j++){
                if(maze[i][j]=='S')
                    sx=i,sy=j;
                vis[i][j]=false;
                ans[i][j]=0;
            }
        ans[sx][sy]=1;
        for(int i=0;i<3;i++)
            num[i]=0;
        ///以上为存图与初始化
        ///判断初始方向
        if(sx==h-1)
            init_dir=0;
        else if(sx==0)
            init_dir=1;
        else if(sy==w-1)
            init_dir=2;
        else if(sy==0)
            init_dir=3;

        success=false;
        dfs_l(sx,sy,init_dir);
        printf("%d",num[0]);
        success=false;
        dfs_r(sx,sy,init_dir);
        printf(" %d",num[1]);
        num[2]=bfs(sx,sy);
        printf(" %d\n",num[2]);

    }
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值