P2864题解

· · 题解

基本思路

bfs的应用范围是找从一个点到另一个点的最短距离,但题目是要从起点绕一圈再回去,所以正好矛盾,所以题有问题,但是,只要在别处选一个点并放个墙,就像下面的图一样:

可以看到计算机就会去绕远道,既然找出了远道的最短路线,再找出到墙下面的最短路线,他俩加起来那不就是最短的路径了吗?

细节处理

关于墙放在哪里:

我个人觉得放在最下面或者最上面树的旁边就好,就像下面这张图片:

可以看到我把墙放在了最下面树的右边,而墙的上面的三个点是我们要比较的较远的路线的步数的点(就是选上面三个点里面步数最小的),而墙的下方是要比较的较近的路线的步数的点(也就是选下面三个点里面最小的),从图中可以看到,从上面点到下面的点一定是要走两步才能链接起来的,所以最后输出的时候要把两条线路较小的加起来再加上个2。

关于queue队列(会的自行跳过):

queue是c++独有的一个stl函数,用它来写bfs简直不要太简单,下面是关于此函数的一些用法:

定义:queue<数据类型>队列名

如:queue<int>q 定义了一个名为q的int类型的队列

q.back() 返回最后一个元素

q.empty() 如果队列空则返回真

q.front() 返回第一个元素

q.pop() 删除第一个元素

q.push() 在末尾加入一个元素

q.size() 返回队列中元素的个数

你问我头文件是什么?那当然是万能头#include<queue>

其实可以用结构体但我不会

关于0x3f3f3f

这个东西就是一个无穷大的值,一开始我用的0,然后全爆空间了。。

代码实现

代码中的墙是放在了最后一颗树的下面,因为比较好处理

数据比较水,这是不特判起点会被墙覆盖,ac代码

#include<bits/stdc++.h> 
using namespace std;
int m,n,ax,ay,bx,by,dis[51][51];//dis是用来存贮步数的 
int dx[8]={0,0,1,-1,1,1,-1,-1};//控制向八个方向移动 
int dy[8]={1,-1,0,0,1,-1,1,-1};
char a[51][51];//存储输入的地图 
void bfs(int x,int y,int z)//此函数用来计算步数 
{
    queue<int>xx;//定义三个数列存横纵坐标和步数 
    queue<int>yy;
    queue<int>zz;
    memset(dis,0x3f3f3f3f,sizeof(dis));//给dis数组都赋一个值,0x3f3f3f3f是一个无穷大的数 
    xx.push(x);//把各项初值给相应的队列 
    yy.push(y);
    zz.push(z);
    dis[x][y]=0;//把起点的步数的值赋上0;因为前面赋了一个无穷大的值 
    while(!xx.empty())//只要队列不空就一直循环 
    {
        int xxx=xx.front();//把最上面的横纵坐标和步数取出来 
        int yyy=yy.front();
        int zzz=zz.front();
        xx.pop();yy.pop();zz.pop();//把刚刚取出来的去掉 
        for(int i=0;i<=7;i++)//从第一个方向开始一个一个走 
        {
            int tx=xxx+dx[i];//计算移动后的横纵坐标 
            int ty=yyy+dy[i];
            if(dis[tx][ty]!=0x3f3f3f3f) continue;//如果等于刚刚赋的值就往下进行循环,否则已经有了值,就直接跳过,开始下一次循环 
            if(a[tx][ty]=='X'||tx<0||ty<0||tx>m||ty>n||a[tx][ty]=='|') continue;//判断是否越界和是否是墙或者树,是就跳过,开始下一次循环 
            xx.push(tx);//把计算出的符合要求的横纵坐标放入队列 
            yy.push(ty);
            dis[tx][ty]=zzz+1;//计算到达此点所用的步数 
            zz.push(zzz+1);//把当前的步数放入zz数列(如果难理解就换成zz.push(dis[tx][ty])  ) 
        }
    }
    return ;//循环完了要退出 
}
int main()
{
    cin>>m>>n;
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>a[i][j];
            if(a[i][j]=='*') ax=i,ay=j;//找到起点(终点)位置 
            if(a[i][j]=='X') bx=i,by=j;//这个主要是找到最后一颗树的位置 
        }
    }
    bx++;//不加一就下面i=bx+1,墙不能建在树上 
    for(int i=bx;i<=m;i++) a[i][by]='|';//建好墙 
    bfs(ax,ay,0);//开始搜索 
    int k1=min(dis[bx-1][by-1],min(dis[bx][by-1],dis[bx+1][by-1]));//找出答案 
    int k2=min(dis[bx-1][by+1],min(dis[bx][by+1],dis[bx+1][by+1]));
    cout<<k1+k2+2<<endl;//输出 
    return 0;//好习惯 
}

下面是特判后的ac代码

#include<bits/stdc++.h>
using namespace std;
int m,n,ax,ay,bx,by,dis[51][51];
int dx[8]={0,0,1,-1,1,1,-1,-1};//控制向八个方向移动 
int dy[8]={1,-1,0,0,1,-1,1,-1};
char a[51][51];
bool flag=0;
void bfs(int x,int y,int z)
{
    queue<int>xx;
    queue<int>yy;
    queue<int>zz;
    memset(dis,0x3f3f3f3f,sizeof(dis));
    xx.push(x);
    yy.push(y);
    zz.push(z);
    dis[x][y]=0;
    while(!xx.empty())
    {
        int xxx=xx.front();
        int yyy=yy.front();
        int zzz=zz.front();
        xx.pop();yy.pop();zz.pop();
        for(int i=0;i<=7;i++)
        {
            int tx=xxx+dx[i];
            int ty=yyy+dy[i];
            if(dis[tx][ty]!=0x3f3f3f3f) continue;
            if(a[tx][ty]=='X'||tx<0||ty<0||tx>m||ty>n||a[tx][ty]=='|') continue;
            xx.push(tx);
            yy.push(ty);
            dis[tx][ty]=zzz+1;
            zz.push(zzz+1);
        }
    }
    return ;
}
int main()
{
    cin>>m>>n;
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>a[i][j];
            if(a[i][j]=='*') ax=i,ay=j;
            if(a[i][j]=='X') bx=i,by=j;
        }
    }
    bx++;
    if(by==ay&&ax>bx)
    {
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
            if(a[i][j]=='X'&&flag==0)
            {
                bx=i;
                by=j;
                flag=1;
                break;
            }
        bx--;
        for(int i=1;i<=bx;i++) a[i][by]='|';
    bfs(ax,ay,0);
    int k1=min(dis[bx-1][by-1],min(dis[bx][by-1],dis[bx+1][by-1]));
    int k2=min(dis[bx-1][by+1],min(dis[bx][by+1],dis[bx+1][by+1]));
    cout<<k1+k2+2<<endl;
    return 0;
    }
    for(int i=bx;i<=m;i++) a[i][by]='|';
    bfs(ax,ay,0);
    int k1=min(dis[bx-1][by-1],min(dis[bx][by-1],dis[bx+1][by-1]));
    int k2=min(dis[bx-1][by+1],min(dis[bx][by+1],dis[bx+1][by+1]));
    cout<<k1+k2+2<<endl;
    return 0;
}