最近在牛客做了几道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;
}
这类题都没有很大的难度,关键就是在于要写的熟练,现在差不多写完保证全对要一个小时的时间,应该熟练到半个小时才算合格。