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标记的问题了,希望自己能够记住这个教训。

全部评论

相关推荐

有没有友友知道这样是开启下一个志愿还是在池子里等人捞
早饭有梨:为什么有的是回到人才池,有的是变成筛选中,我二面挂直接变回筛选中了
点赞 评论 收藏
分享
会飞的猿:我看你想进大厂,我给你总结一下学习路线吧,java语言方面常规八股要熟,那些java的集合,重点背hashmap八股吧,jvm类加载机制,运行时分区,垃圾回收算法,垃圾回收器CMS、G1这些,各种乐观锁悲观锁,线程安全,threadlocal这些。在进阶一些的比如jvm参数,内存溢出泄漏排查,jvm调优。我这里说的只是冰山一角,详细八股可以去网上找,这不用去买,都免费资源。mysql、redis可以去看小林coding,我看你简历上写了,你一定要熟,什么底层b+树、索引结构、innodb、mvcc、undo log、redo log、行级锁表级锁,这些东西高频出现,如果面试官问我这些我都能笑出来。消息队列rabbitmq也好kafka也好,学一种就行,什么分区啊副本啊确认机制啊怎么保证不重复消费、怎么保证消息不丢失这些基本的一定要会,进阶一点的比如LEO、高水位线、kafka和rocketmq底层零拷贝的区别等等。计算机网络和操作系统既然你是科班应该理解起来问题不大,去看小林coding这两块吧,深度够了。spring boot的八股好好看看吧,一般字节腾讯不这么问,其他的java大厂挺爱问的,什么循环依赖啥的去网上看看。数据结构的话科班应该问题不大,多去力扣集中突击刷题吧。项目的话其实说白了还是结合八股来,想一想你写的这些技术会给你挖什么坑。除此之外,还有场景题、rpc、设计模式、linux命令、ddd等。不会的就别往简历上写了,虽然技术栈很多的话好看些,但背起来确实累。总结一下,多去实习吧,多跳槽,直到跳到一个不错的中厂做跳板,这是一条可行的进大厂的路线。另外,只想找个小厂的工作的话,没必要全都照这些准备,太累了,重点放在框架的使用和一些基础八股吧。大致路线就这样,没啥太多难度,就是量大,你能达到什么高度取决于你对自己多狠,祝好。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务