对于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,欢迎踊跃提问
本文仅以个人经历和个人观点作为参考.如能受益,不胜荣幸. 本文会不断的修正,更新.希望通过大家的互动最后能写出一份阅者受益的文章. 本文纯手打,会有错别字,欢迎指出,虚心接受及时更改. 小马过河,大牛 ...
随机推荐
- 【转】Redis一般会遇到的问题以及解析
单线程的 Redis 为什么这么快 这个问题是对 Redis 内部机制的一个考察.根据我的面试经验,很多人都不知道Redis 是单线程工作模型.所以,这个问题还是应该要复习一下的. 回答主要是以下三点 ...
- SpringMVC之入门程序
SpringMVC之入门程序——使用浏览器展示商品数据 springMVC执行流程(图片来源:https://www.jianshu.com/p/8a20c547e245) 1.创建pojo(商品实体 ...
- defer 和 async 区别
defer saync 共同点: script 标签属性, 控制脚本加载时间,解决script下载阻塞的问题. 区别: defer:推推推荐! 异步加载,所有元素解析完执行. async: 异步加载, ...
- 移动端video不全屏播放
<div class="m-video"> <video x5-playsinline="" playsinline="" ...
- Odoo 中使用 celery 实现高性能异步任务队列
详见:http://www.oejia.net/blog/2018/07/09/odoo_task_queue.html 概述 在 odoo 中可以用自带的cron实现异步任务,这个cron基于多线程 ...
- git 提交项目代码到码云步骤 以及出现错误解决办法
git initgit remote add origin 项目地址git add .git commit -m "注释"git push origin master 出现错误 $ ...
- 转int啥啥啥的
1.String转int类型的话.需要用Double.valueof("这写String类型的数据").intValue(); 2.保留小数点: float scale = (fl ...
- git常用命令总结--廖雪峰老师Git教程命令总结
学习了廖雪峰老师的Git教程之后的命令总结,重点关于git和远程仓库的东西. 如果没有学过,这是传送门 下面这个图很重要 一.git初始化本地仓库和配置 echo "想输入到文件的内容,一般 ...
- Scala之eq,equals,==的区别
一.简介 根据官方API的定义: final def ==(arg0: Any): Boolean The expression x == that is equivalent to if (x eq ...
- 安装Docker时错误提示 "could not change group /var/run/docker.sock to docker: group docker not found"的解决方案
安装Dock服务,主要命令是 yum install docker. 但是在启动的时候报错:warning msg="could not change group /var/run/doc ...