2018.9.4南海中学测试T1

**1、栅栏迷宫

田野上搭建了一个黄金大神专用的栅栏围成的迷宫。幸运的是,在迷宫的边界上留出了两段栅栏作为迷宫的出口。更幸运的是,所建造的迷宫是一个“完美的”迷宫:即你能从迷宫中的任意一点找到一条走出迷宫的路。给定迷宫的宽W(1<=W<=38)及长H(1<=H<=100)。 2*H+1行,每行2*W+1的字符以下面给出的格式表示一个迷宫。然后计算从迷宫中最“糟糕”的那一个点走出迷宫所需的步数(就是从最“糟糕”的一点,走出迷宫的最少步数)。(即使从这一点以最优的方式走向最靠近的出口,它仍然需要最多的步数)当然了,黄金大神让你必须只会水平或垂直地在X或Y轴上移动,你不能从来不走对角线。每移动到一个新的方格算作一步(包括移出迷宫的那一步)这是一个W=5,H=3的迷宫:

如上图的例子,栅栏的柱子只出现在奇数行或奇数列。每个迷宫只有两个出口。
PROGRAM NAME: maze
INPUT FORMAT: (file maze.in)
第一行: W和H(用空格隔开)
第二行至第2*H+1行: 每行2*W+1个字符表示迷宫
OUTPUT FORMAT: (file maze.out)
输出一个单独的整数,表示能保证牛从迷宫中任意一点走出迷宫的最小步数。
SAMPLE INPUT
5 3

SAMPLE OUTPUT
9


题目很容易看懂,每个点到最近的出口都有一个最短路径,那么就求出所有点走出最近出口的最短路径的最大值。那么想法很简单,直接两个BFS就ok了。每次BFS保存走到这个格子的最小值,跑完两次之后找最大值就可以了。

然后这道题目和其他题目不同的是,它的图很奇怪,并不是经常见到的01图。观察这个图可得知,其实W一共有2*W+1这么大, H也只是有2*H+1这么大。然后奇数行都是墙,都不是能走的格子。还有一个最重要的问题,这个图之中还有空格。这个让我们保存这个图有一点难度。
所以我就像下面代码一样保存这个图:

    for(int i=1;i<=h;i++)
    {
        getchar();
        char x; 
        for(int j=1;j<=w;j++)
        {
            x=getchar();
            if(x!=' ')
              m[i][j]=true;
        } 
    }

每次读入一个字符,用getchar()不仅能读入空格,也能读入换行符。所以在读入完每一行的数据之后都要getchar()一下。然后就判断读入的是否是空格就ok了。

然后就是保存出口的问题了。这道题我一开始爆零就是因为找出口太片面了,认为出口肯定一个在上下,一个在右,所以两次BFS前现在上下找一个出口,或者在左右找一个出口。
这样明显是错误的嘛,所以找出口在输入的时候加上一条判断,如果是出口就保存,那就ok啦。

if((i==1)&&(x==' ')) {tail++;dl[tail][0]=i+1;dl[tail][1]=j;}
if((i==h)&&(x==' ')) {tail++;dl[tail][0]=i-1;dl[tail][1]=j;}
if((j==1)&&(x==' ')) {tail++;dl[tail][0]=i;dl[tail][1]=j+1;}
if((j==w)&&(x==' ')) {tail++;dl[tail][0]=i;dl[tail][1]=j-1;}

然后我在我错误的程序上修改后,测试完有一个点总是超时。而且这个图也是特别的奇葩。

这个图超级帅,超有规律,但是我就是超时了。比这个数据还大的数据我都不超时,就这个超时了。然后我调试的时候发现,我在BFS时死循环了。然后我看了很久,才发现我犯了一个错误,而且这个错误在我写BFS时也是很常见。
大家先看看我错误的BFS:

void bfs()
{
    memset(vis,false,sizeof(vis));
    while(!q.empty() )
      q.pop() ;
    read_into(a,b);
    ans[a][b]=1;
    while(!q.empty())
    {
        A d=q.front();
        q.pop();
        int x=d.xi,y=d.yi;
        vis[x][y]=true;
        for(int i=0;i<4;i++)
        {
            if(!m[x+DX[i]][y+DY[i]])
            {
                int x_=x+X[i];
                int y_=y+Y[i];
                if(!vis[x_][y_] && ans[x][y]+1<=ans[x_][y_])
                {
                    if(x_>0 && x_<=h && y_>0 && y_<=w)
                    {
                        ans[x_][y_]=min(ans[x_][y_],ans[x][y]+1);
                        read_into(x_,y_);
                    }
                }
            }
        }
    }
}

先解释一下,能走的格子之间是有墙的,所以先判断距离为一的地方是否时墙,如果不是墙表示能走,所以就走两步,这是由于这道题才这么做的。

回归正题,那么这个BFS看上去没错的,但是导致了死循环,为什么呢?
看看我vis数组,标记的位置是在当执行到这个点的时候才标记为true。那么就会出现一个情况,我很多个点都能重复走到一个点,那么我这个点就一直入队,一直入一直入,所以导致了死循环。
解决办法很简单,当发现这个可以走时直接标记为true,这样就不会出现这种重复入队的情况了。

void bfs()
{
    memset(vis,false,sizeof(vis));
    while(!q.empty() )
      q.pop() ;
    read_into(a,b);
    ans[a][b]=1;
    while(!q.empty())
    {
        A d=q.front();
        q.pop();
        int x=d.xi,y=d.yi;

        for(int i=0;i<4;i++)
        {
            if(!m[x+DX[i]][y+DY[i]])
            {
                int x_=x+X[i];
                int y_=y+Y[i];
                if(!vis[x_][y_] && ans[x][y]+1<=ans[x_][y_])
                {
                    if(x_>0 && x_<=h && y_>0 && y_<=w)
                    {
                        ans[x_][y_]=min(ans[x_][y_],ans[x][y]+1);
                        vis[x_][y_]=true;
                        read_into(x_,y_);
                    }
                }
            }
        }
    }
}

这个就是完美的BFS了。

这道题给我最大的教训吧,就是BFS标记的问题了,希望自己能够记住这个教训。

全部评论

相关推荐

不愿透露姓名的神秘牛友
11-21 17:16
科大讯飞 算法工程师 28.0k*14.0, 百分之三十是绩效,惯例只发0.9
点赞 评论 收藏
分享
头像
11-09 12:17
清华大学 C++
out11Man:小丑罢了,不用理会
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务