图的搜索算法 BFS

最近在牛客做了几道BFS的题目,应该是重回经典了,相比之前,也有了一些不一样的感悟,现在写下来记下以往的思考经历。

 

 

        题目的意思是一个人带着鱼可以走r1步数,猫可以闻r2的步数,现在把他们放在一张图下面,询问猫是否存在有吃到鱼的可能,如果有算出他们最小的步数和。本来想的是,可以将两者的步数放到一个人的身上,但是这样就忽略了一个关键信息。图里面存在障碍物,人走不过去障碍物,但是猫闻食物可以越过障碍物。然后看到数据范围比较小,因此,就产生这样一种思路,BFS遍历人,将人所有可能去的地方都给找出来,对于这些地点判断是否能被猫闻到,如果满足上面两个条件,就有可能成为一个合理的答案,在这所有的答案中寻找一个最小的就是答案了。

        具体的代码实现就是首先以P为起点BFS遍历,得到P到其他点的距离图,然后同理,得到M到其他点的距离图。然后遍历n*m图,假如是合理的路径,那么步数和就是两者图的距离和。所以本题没有什么难度,就是手速题,写BFS的熟练度高即可。自己写这个BFS有点慢,下次必须该练一练。

#include <bits/stdc++.h>
using namespace std;
//两个点需要连接 都有各自移动的范围限制 求最短的路径和
//闻的话可以跨过障碍物 但是走不行
//可以让人先走 然后bfs遍历猫 看看能到达哪个 取最小值
char mp[1006][1006];
int n,m;
int dx[4] = {-1,0,1,0},dy[4] = {0,-1,0,1};
int plm[1006][1006];//单源最短路 存储的是起点到里面的点的距离
int cat[1006][1006];
struct point{
    int x,y;
};
point pp[1000006];
point P,M;
//bfs遍历图 求出最短路 
//给出一个起点 并且 将图信息存进相应的图里面
void bfs(point a,int d[1006][1006]){
    queue <point> qp;
    for (int i = 0;i<1006;i++)//初始化距离无限远
    {
        for (int j = 0;j<1006;j++)
            d[i][j] = 1e9+7;
    }
    d[a.x][a.y] = 0;//起点到自身距离为0
    qp.push(a);
    while (!qp.empty())//这里不要错了
    {
        point qi = qp.front();//取出第一个元素
        qp.pop();//释放元素
        for (int i = 0;i<4;i++)//以
        {
            int nx = qi.x + dx[i];
            int ny = qi.y + dy[i];
//             cout << nx <<" " << ny <<"\n";
            if(mp[nx][ny]=='.' && d[nx][ny]==1e9+7 && nx >=1 && ny>=1 && nx<=n && ny <=m)//可以走 且未被访问 必须要是合理的数据
            {
                qp.push({nx,ny});//添加进队列
                d[nx][ny] = d[qi.x][qi.y]+1;
            }
        }
    }
    
}

int dis(point a,point b)
{
    return abs(a.x-b.x) + abs(a.y-b.y);
}

int main()
{
    cin >> n >> m;
    int r1,r2;cin >> r1 >> r2;
    for (int i = 1;i<=n;i++)
    {
        for (int j = 1;j<=m;j++)
        {
            cin >> mp[i][j];
            if (mp[i][j]=='P') P.x = i,P.y = j;
            if (mp[i][j]=='M') M.x = i,M.y = j;
        }
    }
    
    bfs(P,plm);//所有点到P的距离
    bfs(M,cat);//所有点到猫的距离
//     for (int i = 1;i<=n;i++)
//     {
//         for (int j = 1;j<=m;j++)
//         {
//             cout << plm[i][j] << " ";
//         }
//         cout << "\n";
//     }

//     for (int i = 1;i<=n;i++)
//     {
//         for (int j = 1;j<=m;j++)
//         {
//             cout << cat[i][j] << " ";
//         }
//         cout << "\n";
//     }
    
    int ans = 1e9;
     for (int i = 1;i<=n;i++)
    {
        for (int j = 1;j<=m;j++)
        {
            if (plm[i][j] <= r1 && dis(M,{i,j})<=r2)//人可以走到该点 且人到猫的距离也可以闻到
            {
                ans = min(ans,plm[i][j]+cat[i][j]);
            }
        }
       
    }
    if (ans==1e9) 
    {
        cout << -1;
        return 0;
    }
    cout << ans;
    
    return 0;
}

第60场小白月赛D也和上面的类似

本题的意思为从起点到一个中途符合的点,然后再去终点,求最短的路径,每次只能上下左右运动,问题求解也很类似。BFS分别从起点和终点开始搜索,分别建立两个距离图。然后再对所有的中途点进行挑选,找到最小值。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mp[2006][2006];
ll d[2006][2006];
ll n,m,x;
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
//给定一个起点和终点 根据条件寻找最短路


struct point {
    ll x,y;
};

int zd(int qx,int qy,int zx,int zy){
    queue <point>q;
    q.push({qx,qy});//起点
    for (int i = 0;i<2006;i++)
        for (int j = 0;j<2006;j++){
            d[i][j] = 1e9+7;
        }
    
    d[qx][qy] = 0;
    while (q.size()){
        point pt = q.front();q.pop();//取出队首元素
        for(int i = 0;i<4;i++){
            ll nx = pt.x+ dx[i];
            ll ny = pt.y+ dy[i];
            
            if(0 < nx && nx <=n && 0 < ny && ny <= m && mp[nx][ny]!=-1 && d[nx][ny] == 1e9+7)
            {
                q.push({nx,ny});
                d[nx][ny] = d[pt.x][pt.y] + 1;
            }
        }
    }
    return d[zx][zy];
}


vector <point> vp;

int main(){
    cin >> n >> m >> x;
    ll sx,sy,ex,ey;cin >> sx >> sy >> ex >> ey;

    for (int i = 1;i<=n;i++){
        for (int j = 1;j<=m;j++){
            cin >> mp[i][j];
            if (mp[i][j]>x){
            vp.push_back({i,j});  
            }
        }
    }
    if (vp.size()==0) {
        cout << "-1";
        return 0;
    }
    ll ans = 1e9+7;
    
    ll d1[2006][2006];
    
    ll xx = vp[0].x;
    ll yy = vp[0].y;
    zd(sx,sy,xx,yy);
    for (int i = 0;i<2006;i++){
        for (int j = 0;j<2006;j++)
            d1[i][j] = d[i][j];
    }
    zd(ex,ey,xx,yy);
    
    
    for (int i = 0;i<vp.size();i++){
        
//         cout << zd(sx,sy,xx,yy)+zd(xx,yy,ex,ey);
        xx = vp[i].x;
        yy = vp[i].y;
        ans = min(d[xx][yy]+d1[xx][yy],ans);
    }
    if (ans==1e9+7){
        cout <<"-1";
        return 0;
    }
    cout << ans;
    
    return 0;
}

 这类题都没有很大的难度,关键就是在于要写的熟练,现在差不多写完保证全对要一个小时的时间,应该熟练到半个小时才算合格。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值