对于BFS的理解和部分例题(
(图文无关 雾
搜索是一个NOIP当中经常出现的考点,其实搜索换个方式来想也无非就是让电脑来帮你试,最后得到一个结果,当然这么口胡谁都会,那么我们就来看看搜索当中的一个大部分:
BFS(广度优先搜索)
首先我们看这样一道例题
P1451 求细胞数量
这个题吧,其实是我学习BFS遇到的第一道题,当然他作为一个很好的入门题让我很快的学会了BFS的基本模板
大概总结一下就是这样的
struct node{
// 你要表示的状态,例如:坐标
}
node _node(/*参数*/){
node r;
//初始化
return r;
}
/*
例如:
struct node{
int x,y;
}
node _node(int x,int y){
node r;
r.x=x;
r.y=y;
return r;
}
*/
queue<node>q;
bool vis[...]...[...];//判重数组,保证每个状态只访问一次。
void bfs(node s/*起始元素*/){
q.push(s);
vis[...]...[...]=;//初始状态纪录
while(!q.empty()){
node head=q.front();
q.pop();
for(...){//遍历所有能到达的状态
node new=_node(head...)//新状态
if(...new...&&!vis[...]...[...]){//新状态合法且没被访问过
q.push(new);
vis[...]...[...]=;//新状态纪录。
}
}
}
}
当然还是WZ大佬比较强%%%%%%%%%
然后吧我们来看一看这个题
其实是这样的
我们先写好上下左右四个方向的代码
int dx[] = {-, , , },
dy[] = {, , , -};
然后再计算的时候用一个for循环实现就行了具体的思路是这样的
找到一个点的x,y---->对于这个点上下左右(四联通)进行判断,如果是细胞就把其坐标压进队列,如果不是就跳过,当我们处理完一个细胞(假设他周围有细胞的话),就会继续处理它周围的细胞,知道把所有细胞的四联通都找完为止,对了我们把一个细胞找完之后要把它清为false,不然会出错
上代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
bool b[][];
int n, m, a[][], ans = ;
int dx[] = {-, , , },
dy[] = {, , , -};
inline void qaq(int x, int y)
{
int hx[], hy[], head = , tail = , tx, ty;
ans++;
hx[] = x, hy[] = y;
b[x][y] = false;
for (; head <= tail; ++head)
{
for (int i = ; i <= ; ++i)
{
tx = hx[head] + dx[i],
ty = hy[head] + dy[i];
if (tx > && tx <= m && ty > && ty <= n && b[tx][ty])
{
tail++;
hx[tail] = tx,
hy[tail] = ty;
b[tx][ty] = false;
}
}
}
}
int main()
{
scanf("%d%d", &m, &n);
for (int i = ; i <= m; ++i)
for (int j = ; j <= n; ++j)
b[i][j] = true;
for (int i = ; i <= m; ++i)
for (int j = ; j <= n; ++j)
{
scanf("%1d", &a[i][j]);
if (!a[i][j])
b[i][j] = false;
}
for (int i = ; i <= m; ++i)
for (int j = ; j <= n; ++j)
if (b[i][j])
qaq(i, j);
printf("%d", ans);
return ;
}
T2
P1162 填涂颜色
这个题其实和上一道差不多,也是找连通块然后进行处理,那么做这个题的时候我们需要考虑些什么呢?
首先,因为涂色区域一定是被包围着的,所以他一定是2~~~~n-1这个范围内的,循环的时候就可以这么写(不过不影响大局)
然后他说只把中间那个区域涂上颜色,换个思路就是我们把所有边上的连通块全都处理掉不就好了嘛~
上代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, a[][], dx[] = {, , -, }, dy[] = {, , , -}, X, Y;
bool b[][];
void qaq(int x, int y)
{
int q[][], head = , tail = ;
tail++;
q[tail][] = x;
q[tail][] = y;
b[x][y] = ;
for (; head <= tail; ++head)
{
for (int i = ; i <= ; ++i)
{
X = q[head][] + dx[i];
Y = q[head][] + dy[i];
if (X > && X <= n && Y > && Y <= n && b[X][Y] == )
{
tail++;
q[tail][] = X;
q[tail][] = Y;
b[X][Y] = ;
}
}
}
}
int main()
{
scanf("%d", &n);
for (int i = ; i <= n; ++i)
for (int j = ; j <= n; ++j)
{
scanf("%d", &a[i][j]);
if (a[i][j])
b[i][j] = ;
}
//printf("\n\n\n\n\n\n");
for (int i = ; i <= n; ++i)
for (int j = ; j <= n; ++j)
if (!a[i][j] && (i == || j == || i == n || j == n))//这里就是保证连通块必然有至少一个数字在边上
qaq(i, j);
for (int i = ; i <= n; ++i)
{
for (int j = ; j <= n; ++j)
{
if (!b[i][j])
printf("2 ");
else
printf("%d ", a[i][j]);
}
printf("\n");
}
}
现在我们来看BFS的另外一个大类型
迷宫问题~~~~~
说真的,做到这东西的时候,我的内心是这样的
因为迷宫我一开始不会,就先做了神奇题目_GC滑迷宫(毕竟是自己参与题目构思的嘛~)
题目有限制同学们就自己去团队里头找就好啦
首先这个题的思路是把地图读入,然后从起始坐标开始搜
因为_GC只能滑着走啊,所以这货就直接让他撞墙(a[x][y]==1)的时候换个方向继续划水就行了
有两个操作比较好
判出界操作和判初始位置这个操作(虽然正常人测试点不会这么诡异)
上代码(虽然黈力,WZ别打我)
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
int n, m, sx, sy;
bool mat[][];
const int dx[] = {, , , -}, dy[] = {, , -, };
struct pos
{
int x, y, step;
pos(int x, int y, int step) : x(x), y(y), step(step) {}
};
queue<pos> q;
bool vis[][];
inline bool pan(int x, int y)//判断合法性
{
return x >= && x <= n && y >= && y <= m && mat[x][y] == ;
}
bool out(int x, int y)//判断是否已到达迷宫边界,即可以离开迷宫
{
return (x == || x == n || y == || y == m) && mat[x][y] == ;
}
int main()
{
cin >> n >> m;
for (int i = ; i <= n; i++)
{
for (int j = ; j <= m; j++)
{
cin >> mat[i][j];
}
}
cin >> sx >> sy;
if(mat[sx][sy]){//如果初始位置是墙,则无解
cout<<-;
return ;
}
if(out(sx,sy)){//如果初始位置在边界,则直接离开
cout<<;
return ;
}
q.push(pos(sx, sy, ));
vis[sx][sy] = ;
while (!q.empty())
{
pos h = q.front();
q.pop();
// cout << h.x << ' ' << h.y << endl;
if (out(h.x, h.y))//如果到达出口
{
cout << h.step - ;//因为原题求的是撞墙次数,而我们知道最后一次是不撞墙的
return ;//直接退出,因为已求得最优解
}
for (int i = ; i < ; i++)
{
int xx = h.x, yy = h.y;
while (pan(xx + dx[i], yy + dy[i]))//如果没撞到墙,就继续走
{
xx += dx[i];
yy += dy[i];
}
if ((xx == h.x && yy == h.y) || vis[xx][yy])//如果并没有移动,或者最终位置已到达过
continue;
vis[xx][yy] = ;
q.push(pos(xx, yy, h.step + ));
}
}
cout << -;//如果所有可能节点都已遍历,而始终没有到达边界,则无解
}
在慢慢的看完了_GC滑迷宫之后,我开始试着做迷宫了,
P1605 迷宫
迷宫的操作其实就是把滑的部分简化,这里直接用到连通块那个dx和dy就行
对于每一个点都进行四联通操作,然后递归的来模拟走的路线,因为是递归求解,每一个方向只走一次,所以路线就不会重复,如果到了终点,我们就把路线+1
代码如下
#include <iostream>
#include <cstdio>
using namespace std;
int n, m, t, ex, ey, ans = , bx, by, tx, ty, X, Y;
int dx[] = {, , , -};
int dy[] = {-, , , };
bool pos[][], map[][];
void qaq(int x, int y)
{
if (x == ex && y == ey)
{
ans++;
return;
}
else
{
for (int i = ; i <= ; ++i)
{
X = x + dx[i];
Y = y + dy[i];
if (!pos[X][Y] && map[X][Y] && X<=m && x> && y<=n && Y>) //没被走过而且不是障碍
{
pos[x][y] = ;
qaq(X, Y);
pos[x][y] = ;
}
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &t);
for (int i = ; i <= n; ++i)
for (int j = ; j <= m; ++j)
map[i][j] = true;
scanf("%d%d%d%d", &bx, &by, &ex, &ey);
for (int i = ; i <= t; ++i)
{
scanf("%d%d", &tx, &ty);
map[tx][ty] = false; //障碍
}
qaq(bx, by);
printf("%d", ans);
return ;
}
/*
5 5 0
1 1 2 2 4846
*/
这里吧,有一个很坑的点,我因为一开始写的太快了,就忘了加上判边界的情况,所以就只得了80,这也算是一个教训,因为小样例一般比较简单也没什么坑,但是以后写题的时候还是加上更多的判断比较好
接下来我们看一个题
P1141 01迷宫
这个题的题面就很有意思,我是读了好长时间才读懂,关键是吧,他给的样例又非常水,不知道到底应该怎么算,我后来向JYY神仙讨教一番才知道是这么算
这是个例子:
1100
0101
0011
1010 我们看(1,1)这个点吧,它能到达的点数总共有8个,分别是本身,(1,4)(2,1),(2,2),(2,3),(2,4),(3,2),(3,3)
仔细看一看的话,我们就可以用搜索连通块的知识来求这些数量。
这个时候,联系着上面几个已经有的例子,我们就把代码构建的差不多了
这个时候,开心的交上代码,结果看到了这个。。。。。。
我们来看看这个TLE的代码
#include<cstdio>
using namespace std;
int n,m,i,j,x,y,s,t=,f[][]={{,},{,},{,-},{-,}};
int a[][],b[][];
char c[];
void Dfs(int x,int y)
{
b[x][y]=t;
for(int k=;k<=;k++)
{
int tx=x+f[k][],ty=y+f[k][];
if(!(tx<||tx>n||ty<||ty>n||b[tx][ty]==t||a[tx][ty]==a[x][y]))
{
s++;
b[tx][ty]=t;
Dfs(tx,ty);
}
}
return ;
}
int main()
{
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
{
scanf("%s",c);
for(j=;j<=n;j++)
a[i][j]=c[j-];
}
while(m--)
{
scanf("%d%d",&x,&y);
Dfs(x,y);
printf("%d\n",s+);
s=;
t++;
}
return ;
}
当然我不会告诉你这是_GC的代码。。。。。。。
我们仔细研究一下这个代码,会发现这个代码对于每一组数据的处理是读进来算完再输出,当然这个方法好想也好实现,数据不是太变态也问题不大,但是架不住你谷有卡到数据范围的测评点啊!!!!!!!!
这玩意直接爆时间啊!!!!!!!
我们得想一想怎么优化这个东西
啊终于想出来了(@某位大学数学讲师)
我们可以用预处理的方法来解决,首先我们会发现同一个连通块上所有的点能到达的点的数量是一样的,那么就简单了呀,我们按照题目要求,先跑一遍所有的连通块,把这个数值赋给每一个其中的元素,然后他问哪个就输出哪个不就OK了嘛~
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
struct point
{
int x, y;
point(int a, int b) : x(a), y(b) {}
};
const int maxn = ;
char _map[maxn][maxn]; //存储地图
int dx[] = {, , , -};
int dy[] = {-, , , };
int pos[maxn][maxn]; //给搜索过的位置作标记
int n, m, i, j;
int ans[maxn * ] = {}; //存储第t次搜索的结果
int qaq(int x, int y, int t)
{
int count = ;
queue<point> q;
point p(x, y);
q.push(p);
while (!q.empty())
{
p = q.front();
//cout<<p.x<<" "<<p.y<<endl;测试用的
q.pop();
for (i = ; i < ; i++)
{
int tx = p.x + dx[i];
int ty = p.y + dy[i];
if (tx > && tx <= n && ty > && ty <= n && _map[p.x][p.y] != _map[tx][ty] && pos[tx][ty] == )
{
point p1(tx, ty);
q.push(p1);
pos[tx][ty] = t;
count++;
}
}
}
return count;
}
int main()
{
cin >> n >> m;
for (i = ; i <= n; i++)
cin >> _map[i] + ;
for (int k = ; k <= m; k++)
{
int x, y;
cin >> x >> y;
if (!pos[x][y]) //没有被搜索过,再搜索一遍
{
pos[x][y] = k;
ans[k] = qaq(x, y, k);
cout << ans[k] << endl;
}
else //被搜索过的话,直接输出结果
{
int t = pos[x][y];
ans[m] = ans[t];
cout << ans[m] << endl;
}
}
return ;
} /*有一个仅由数字0与1组成的n×n格迷宫。
若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,
同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上。
你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。
1100
0101
0011
1010
*/
最后说一句,预处理这玩意真的挺好用的,尤其是多组数据多次询问,简直必备佳品啊!!!!!!!!!
对于BFS的理解和部分例题(的更多相关文章
- 递归,回溯,DFS,BFS的理解和模板
LeetCode 里面很大一部分题目都是属于这个范围,例如Path Sum用的就是递归+DFS,Path Sum2用的是递归+DFS+回溯 这里参考了一些网上写得很不错的文章,总结一下理解与模板 递归 ...
- 递归,回溯,DFS,BFS的理解和模板【摘】
递归:就是出现这种情况的代码: (或者说是用到了栈) 解答树角度:在dfs遍历一棵解答树 优点:结构简洁缺点:效率低,可能栈溢出 递归的一般结构: void f() { if(符合边界条件) { // ...
- 关于DFS和BFS的理解 以及坐标的定义
http://blog.csdn.net/bool_isprime/article/details/5803018DFS: 1: 坐标类型搜索 :这种类型的搜索题目通常来说简单的比较简单,复杂的通常在 ...
- BFS寻路算法的实现
关于BFS的相关知识由于水平有限就不多说了,感兴趣的可以自己去wiki或者其他地方查阅资料. 这里大概说一下BFS寻路的思路,或者个人对BFS的理解: 大家知道Astar的一个显著特点是带有启发函数, ...
- BFS寻路的AS3实现
关于BFS的相关知识由于水平有限就不多说了,感兴趣的可以自己去wiki或者其他地方查阅资料. 这里大概说一下BFS寻路的思路,或者个人对BFS的理解: 大家知道Astar的一个显著特点是带有启发函数, ...
- [学习笔记] CDQ分治 从感性理解到彻底晕菜
最近学了一种叫做CDQ分治的东西...用于离线处理一系列操作与查询似乎跑得很快233 CDQ的名称似乎源于金牌选手陈丹琦 概述: 对于一坨操作和询问,分成两半,单独处理左半边和处理左半边对于右半边的影 ...
- BFS算法(——模板习题与总结)
首先需要说明的是BFS算法(广度优先算法)本质上也是枚举思想的一种体现,本身效率不是很高,当数据规模很小的时候还是可以一试的.其次很多人可能有这样的疑问,使用搜索算法的时候,到底选用DFS还是BFS, ...
- 离散化+BFS HDOJ 4444 Walk
题目传送门 /* 题意:问一个点到另一个点的最少转向次数. 坐标离散化+BFS:因为数据很大,先对坐标离散化后,三维(有方向的)BFS 关键理解坐标离散化,BFS部分可参考HDOJ_1728 */ # ...
- 湾区求职分享:三个月刷题拿到 Google offer,欢迎踊跃提问
本文仅以个人经历和个人观点作为参考.如能受益,不胜荣幸. 本文会不断的修正,更新.希望通过大家的互动最后能写出一份阅者受益的文章. 本文纯手打,会有错别字,欢迎指出,虚心接受及时更改. 小马过河,大牛 ...
随机推荐
- OSS上传文件到阿里云
最近做项目,需要上传文件,因为上传到项目路径下,感觉有时候也挺不方便的,就试了一下上传文件到阿里云oss上去了, oss的使用网上有很多介绍,都是去配置一下需要的数据,然后直接调用他的api就可以了. ...
- ideal中spring的xml文件没有提示的问题
ideal中spring的xml文件没有提示的问题 今天第一次用ideal来练习spring,发现和视频中老师不一样,我的没有提示.老师的视频里,他写了个<mvc:a 就会有一系列的提示,然 ...
- 视频文件列表hover添加视频播放按钮
默认效果图: 鼠标hover效果: 代码如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8 ...
- 20190422-外部导入CSS样式之link、CSS@import、Sass分音
写在前面乱七八糟的前言:今天wuliMR黄问了一个问题,Sass的分音与link标签都是导入外部样式的,有什么不同,这真是个好问题,因为本白着实没想过,也不知道,不过没关系,成功的背后总有一个默默无闻 ...
- Ajax的面试题
一.什么事Ajax?为什么要用Ajax?(谈谈对Ajax的认识) 什么是Ajax: Ajax是“Asynchronous JavaScript and XML”的缩写.他是指一种创建交互式网页应用的网 ...
- vue学习之vuex
1 首先还是安装 npm install vuex --save. 2 在src这种创建目录为store 创建 index.js (getters.js ,actions.js ,mutation ...
- 第七课 路径与列表 html5学习2
1.路径 一.相对路径1.同级路径2.下级路径 /3.上级路径 ../上一级路径 ../../上两级二.绝对路径 2.列表 列表特点;整齐.整洁.有序 一.无序列表语法格式<ul> < ...
- Android远程桌面助手之功能简介
外国友人录制的ARDC的使用简介,非常不错,介绍得很详尽.
- 轻松学习UML之类图,状态图
本文主要讲解UML图中的类图与状态图相关内容,如有不足之处,还请指正. 概述 统一建模语言(UML,UnifiedModelingLanguage)是面向对象软件的标准化建模语言,UML因其简单.统一 ...
- JS中 confirm() 方法
前言 环境: window 10,google 浏览器 测试代码 <html> <!-- 测试确定框,如果点 "是" ,则返回 true,这样就触发 a 标签的 ...