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

全部评论

相关推荐

从输入URL到页面加载发生了什么:总体来说分为以下几个过程:&nbsp;1.DNS解析&nbsp;2.TCP连接&nbsp;3.发送HTTP请求&nbsp;4.服务器处理请求并返回HTTP报文&nbsp;5.浏览器解析渲染页面&nbsp;6.连接结束。简述了一下各个过程的输入输出作用:以下是对从输入&nbsp;URL&nbsp;到页面加载各过程的输入、输出或作用的一句话描述:DNS&nbsp;解析:&nbsp;输入:用户在浏览器地址栏输入的域名(如&nbsp;www.example.com)。输出:对应的&nbsp;IP&nbsp;地址(如&nbsp;192.168.1.1)。作用:将易于记忆的域名转换为计算机能够识别和用于网络通信的&nbsp;IP&nbsp;地址,以便浏览器与目标服务器建立连接。TCP&nbsp;连接:&nbsp;输入:浏览器获得的服务器...
明天不下雨了:参考一下我的说法: 关键要讲出输入网址后涉及的每一个网络协议的工作原理和作用: 涉及到的网络协议: HTTP/HTTPS协议->DNS协议->TCP协议->IP协议->ARP协议 面试参考回答: 第一次访问(本地没有缓存时): 一般我们在浏览器地址栏输入的是一个域名。 浏览器会先解析 URL、解析出域名、资源路径、端口等信息、然后构造 HTTP 请求报文。浏览器新开一个网络线程发起HTTP请求(应用层) 接着进行域名解析、将域名解析为 IP 地址 浏览器会先检查本地缓存(包括浏览器 DNS 缓存、操作系统缓存等)是否已解析过该域名 如果没有、则向本地 DNS 服务器请求解析; 本地服务器查不到会向更上层的 DNS 服务器(根域名服务器->顶级域名服务器->权威域名服务器询问)递归查询 最终返回该域名对应的 IP 地址。(应用层DNS协议)DNS 协议的作用: 将域名转换为 IP 地址。 由于 HTTP 是基于 TCP 传输的、所以在发送 HTTP 请求前、需要进行三次握手、在客户端发送第一次握手的时候、( 浏览器向服务器发送一个SYN(同步)报文、其中包含客户端的初始序列号。TCP头部设置SYN标志位、并指定客户端端口 同时填上目标端口和源端口的信息。源端口是浏览器随机生成的、目标端口要看是 HTTP 还是 HTTPS、如果是 HTTP 默认目标端口是 80、如果是 HTTPS 默认是 443。(传输层) 然后到网络层:涉及到(IP协议) 会将TCP报文封装成IP数据包、添加IP头部,包含源IP地址(浏览器)和目标IP地址(服务器)。IP 协议的作用: 提供无连接的、不可靠的数据包传输服务。 然后到数据链路层、会通过 ARP 协议、获取目标的路由器的 MAC 地址、然后会加上 MAC 头、填上目标 MAC 地址和源 MAC 地址。 然后到物理层之后、直接把数据包、转发给路由器、路由器再通过下一跳、最终找到目标服务器、然后目标服务器收到客户的 SYN 报文后,会响应第二次握手。 当双方都完成三次握手后、如果是 HTTP 协议、客户端就会将 HTTP 请求就会发送给目标服务器。如果是 HTTPS 协议、客户端还要和服务端进行 TLS 四次握手之后、客户端才会将 HTTP 报文发送给目标服务器。 目标服务器收到 HTTP 请求消息后、就返回 HTTP 响应消息、浏览器会对响应消息进行解析渲染、呈现给用户
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务