噔哒!蒟蒻登场,在上一篇文章当中,我们讲了洛谷里面的一些关于广度优先搜索的题目,但难度较低,虽然本篇中的题目难度也不会高到哪去,但至少相比于上篇是有所增加的,接下来讲的是两篇普及/提高-的广搜题目,废话不多说,上题。
本篇也还是讲洛谷中关于bfs的题目。
1.P1443 马的遍历
我们照常先看题目:
题目很短,相比于上篇中的字数要少,看起来简洁明了(实际上是本蒟蒻的阅读能力不强,[忏悔][忏悔])。于是我就将输入输出样例和数据范围也截了,给出来。
由题可知,告诉你有一个n*m的棋盘,而给定马的坐标(x,y),让你打印马在这个棋盘上的每个点的步数。呃呃呃,别人都是给图,这题目是要输出图,有点不按套路出牌。但其实也不难,接下来就是重点了,家人们做笔记。
思路分析:
先定义一张地图mp[][],注意:用int类型。和往常一样,用dx[],dy[]数组分别设定八个方向,再用STL里面的queue队列,从节点(x,y)中不断拓展,搜索。不过特别注意:超出n或m的节点直接放弃,否则可能会使覆盖原本节点的步数,或者也可以理解为棋盘只有n*m这么大,如果超了就飞出棋盘了。这题和上篇中的第一道题一样,不用return,一直搜索到队空为止。
最后就是处理走不到的节点,走不到的节点的步数一定是0的,我们只需要将地图中的0全部改为-1,不过要将mp[x][y]改为0,毕竟,(x,y)是起点。
分析完毕,上代码:
#include<iostream>
#include<queue>
using namespace std;
struct node{
int x,y;
void fill(int a,int b){
x=a;
y=b;
}
};
bool vis[405][405];
int mp[405][405];
int n,m,x,y;
int dx[8]={-2,-1,1,2,2,1,-1,-2};
int dy[8]={-1,-2,-2,-1,1,2,2,1};
void bfs(int x,int y){
for(int i=0;i<405;i++){
for(int j=0;j<405;j++){
mp[i][j]=0;
}
}
queue<node> q;
node fron,now;
fron.fill(x,y);
mp[x][y]=0;
vis[x][y]=true;
q.push(fron);
while(!q.empty()){
fron=q.front();
q.pop();
for(int i=0;i<8;i++){
now.x=fron.x+dx[i];
now.y=fron.y+dy[i];
if(now.x<1||now.y<1||vis[now.x][now.y]||now.x>n||now.y>m)continue;
vis[now.x][now.y]=true;
mp[now.x][now.y]=mp[fron.x][fron.y]+1;
q.push(now);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(mp[i][j]==0)mp[i][j]=-1;
}
}
mp[x][y]=0;
}
int main(){
cin>>n>>m>>x>>y;
bfs(x,y);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cout<<mp[i][j]<<" ";
}
if(i!=n)cout<<endl;
}
return 0;
}
这就是完整的代码,直接AC掉。
不过在最后处理没走到的节点的时候也可以顺便输出,可以优化一下,但时间复杂度相差不大。优化方法:
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(mp[i][j]==0)mp[i][j]=-1;
if(i==x&&j==y)mp[i][j]=0;
cout<<mp[i][j]<<" ";
}
if(i!=n)cout<<endl;
}
将函数bfs中的最后处理没走到的节点可以改成这一段,这样调用完bfs函数后就不用再输出了。
别说,其实挺简单的,只要思想不滑坡,校长给你当老婆。(滑稽)(滑稽)(滑稽)我一个兄弟编的下句,还挺顺口。
2.1135 奇怪的电梯
接下来,就让我们看看这电梯有多奇怪。
读题,先消化消化。哎,也不知道是哪里的电梯,这么奇怪,要不然就没有这道题了。
再看看输入输出样例和数据范围。
熟练的朋友们肯定都要吐槽了,这都什么题目,这么简单(捂脸)(捂脸)(捂脸),本蒟蒻也没办法,实力不允许。
初学者也可以先尝试一下再看代码,代码不长。
在这篇广搜的题目中,很容易发现,什么二维mp[][],什么dx[],dy[]数组,三个字,根本根本根本不需要,关于怎么拓展下一个节点嘛,那就看题目怎么给了。
思路分析:
定义一个n,再输入相关的数据,照旧用vis[]数组判断是否搜索过,这次可方便多了,只用一维的即可。关键就在于步数的统计,以及拓展节点判断条件。
(1)步数统计
在上一篇中我们也有讲过怎么统计,这次我们细讲一下。首先从第一步开始,步数为0,用queue队列不断拓展,那么新的节点的步数和原本节点的步数的关系就是+1,我们也可以看出它的权值,所以,得出结论:新节点的步数=原本节点的步数+1。
(2)判断条件
在本题,判断条件其实也和普通的bfs题目差不多,但它多了两个限制:
1.没有0或-1,-2,-3等层;
2.没有高于n的层数;
只要处理好这两个条件,本题就基本上解决了。
最后一步,只需要本蒟蒻上代码:
#include<iostream>
#include<queue>
using namespace std;
int n,A,B;
struct node{
int k,step=0,id;
}a[205];
bool vis[205];
void bfs(int s,int t){
queue<node> q;
q.push(a[s]);
vis[s]=true;
while(!q.empty()){
node fron=q.front();
q.pop();
if(fron.id==t){
cout<<fron.step;
return ;
}
if(!vis[fron.id+a[fron.id].k]&&fron.id+a[fron.id].k<=n){
vis[fron.id+a[fron.id].k]=true;
node now;
now.step=fron.step+1;
now.id=fron.id+a[fron.id].k;
if(now.id==t){
cout<<now.step;
return ;
}
q.push(now);
}
if(!vis[fron.id-a[fron.id].k]&&fron.id-a[fron.id].k>0){
vis[fron.id-a[fron.id].k]=true;
node now;
now.step=fron.step+1;
now.id=fron.id-a[fron.id].k;
if(now.id==t){
cout<<now.step;
return ;
}
q.push(now);
}
}
cout<<-1;
return;
}
int main(){
cin>>n>>A>>B;
for(int i=1;i<=n;i++){
cin>>a[i].k;
a[i].id=i;
}
bfs(A,B);
return 0;
}
完整代码如上,本篇就先讲到这。
今天本蒟蒻和昨天一样,讲了两题,csp-j/s的时间越来越近了,希望看到这篇文章的兄弟们能取得好成绩,也希望家人们祝福我可以顺利晋级。
感谢家人们给我的支持与鼓励,阿里嘎多。
(侵删)