BFS和队列
深度优先搜索(DFS)和广度优先搜索(BFS)是基本的暴力技术,常用于解决图、树的遍历问题。
首先考虑算法思路。以老鼠走迷宫为例:
(1):一只老鼠走迷宫。它在每个路口都选择先走右边,直到碰壁无法继续前进,然后回退一步,这一次走左边,接着继续往下走。用这个办法能走遍所有的路,而且不会重复。这个思路就是DFS。
(2):一群老鼠走迷宫。假设老鼠是无限多的,这群老鼠进去后,在每个路口派出部分老鼠探索没有走过的路。走某条路的老鼠,如果碰壁无法前进,就停下;如果到达的路口已经有其他的老鼠探索过了,也停下。很显然,所有的道路都会走到,而且不会重复。这个思路就是BFS。
There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A man is standing on a black tile. From a tile, he can move to one of four adjacent tiles. But he can't move on red tiles, he can move only on black tiles.Write a program to count the number of black tiles which he can reach by repeating the moves described above.
InputThe input consists of multiple data sets. A data set starts with a line containing two positive integers W and H; W and H are the numbers of tiles in the x- and y- directions, respectively. W and H are not more than 20.
There are H more lines in the data set, each of which includes W characters. Each character represents the color of a tile as follows.
'.' - a black tile
'#' - a red tile
'@' - a man on a black tile(appears exactly once in a data set)
OutputFor each data set, your program should output a line which contains the number of tiles he can reach from the initial tile (including itself).
Sample Input6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
11 6
..#..#..#..
..#..#..#..
..#..#..###
..#..#..#@.
..#..#..#..
..#..#..#..
7 7
..#.#..
..#.#..
###.###
...@...
###.###
..#.#..
..#.#..
0 0Sample Output
45
59
6
13
题目大意:“#”相当于不能走的陷阱或墙壁,“.”是可以走的路。从@点出发,统计所能到达的地点总数 代码一:这是初学并查集时硬着头皮用并查集的算法解决了BFS的问题
#include<iostream>
using namespace std; const int h = ;
char map[h][h];
int key[h*h];
int rrank[h*h];
int n,m,dx,dy; int find(int a){
return a==key[a]? a : key[a]=find(key[a]);
} void key_union(int a,int c){
int fa = find(a);
int fc = find(c);
if(rrank[fa]>rrank[fc])
key[fc] = fa;
else{
key[fa] = fc;
if(rrank[fa]==rrank[fc])
rrank[fc]++;
}
} int num(int a){
int k = find(a);
int ans = ;
for(int i=;i<=m;i++)
for(int j=;j<=n;j++)
if(find(i*n+j)==k)
ans++; return ans;
} int main()
{
while(scanf("%d %d",&n,&m)!=EOF){
if(n==&&m==) break;
for(int i=;i<=m;i++){
cin.get();
for(int j=;j<=n;j++){
scanf("%c",&map[i][j]);
if(map[i][j]!='#') key[i*n+j] = i*n+j;
else key[i*n+j] = ;
if(map[i][j]=='@'){//找到@的坐标
dx = i;
dy = j;
map[i][j] = '.';
}
}
} for(int i=;i<m;i++){
for(int j=;j<n;j++){
if(key[i*n+j]){
if(key[i*n+j+])
key_union(i*n+j,i*n+j+);
if(key[i*n+n+j])
key_union(i*n+n+j,i*n+j);
}
}
if(key[i*n+n])
if(key[i*n+*n])
key_union(i*n+*n,i*n+n);
}
for(int i=;i<n;i++)
if(key[m*n+i])
if(key[m*n+i+])
key_union(m*n+i,m*n+i+); int ans = num(dx*n+dy);
printf("%d\n",ans);
}
}
代码二:这是还分不清DFS和BFS时写的DFS算法(比后面的BFS要简洁很多,不知道为什么作为BFS的例题放在这里)
#include<iostream>
using namespace std; int mov[][] = {-,,,,,-,,};
int sum,w,h;
char s[][]; void dfs(int x,int y){
sum++;//计数
s[x][y] = '#';
for(int i=;i<;++i){//四个方向前进
int tx = x+mov[i][];
int ty = y+mov[i][]; if(s[tx][ty]=='.' && tx>= && tx<h && ty>= && ty<w)
dfs(tx,ty);//判断该点可行后进入dfs
}
} int main()
{
int x,y;
while(scanf("%d %d",&w,&h)!=EOF){
if(w==&&h==) break;
for(int i=;i<h;i++){
cin.get();
for(int j=;j<w;j++){
scanf("%c",&s[i][j]);
if(s[i][j]=='@'){//起点
x = i;
y = j;
}
}
}
sum = ;
dfs(x,y);
printf("%d\n",sum);
}
return ;
}
代码三:引自《算法竞赛入门到进阶》中的解题代码 BFS算法
#include<bits/stdc++.h>
using namespace std; char room[][];
int dir[][] = { //左上角的坐标是(0,0)
{-, }, //向左
{, -}, //向上
{, }, //向右
{, -} //向下
}; int Wx, Hy, num;
#define check(x, y)(x<Wx && x>=0 && y>=0 && y<Hy) //是否在room中
struct node{int x, y}; void BFS(int dx, int dy){
num = ;
queue<node> q;
node start, next;
start.x = dx;
start.y = dy;
q.push(start);//插入队列 while(!q.empty()){//直到队列为空
start = q.front();//取队首元素,即此轮循环的出发点
q.pop();//删除队首元素(以取出) for(int i=; i<; i++){//往左上右下四个方向逐一搜索
next.x = start.x + dir[i][];
next.y = start.y + dir[i][];
if(check(next.x, next.y) && room[next.x][next.y]=='.'){
room[next.x][next.y] = '#';//标记已经走过
num ++;//计数
q.push(next);//判断此点可行之后,插入队列,待循环判断
}
}
}
} int main(){
int x, y, dx, dy;
while(~scanf("%d %d",&Wx, &Hy)){
if(Wx== && Hy==)
break;
for(y=; y<Hy; y++){
for(x=; x<Wx; x++){
scanf("%d",&room[x][y]);
if(room[x][y] == '@'){//找到起点坐标
dx = x;
dy = y;
}
}
}
num = ;//初始化
BFS(dx, dy);
printf("%d\n",num);
}
return ;
}
这里暂时整理出了此题的关于DFS和BFS算法的代码,DFS相对好理解,递归的思想早有接触,相对易用;BFS还涉及到queue队列的知识,初学时理解困难,即使此处整理出,也不能保证下次遇到时还能写的出来。
代码一用的是并查集的思想,因为第一次做这个题目的时候刚学并查集,新鲜就拿出来用了,确实是磨破了头皮,尤其当看到DFS的代码以后,我现在再拿出来都不敢相信这是我当时写的代码?并查集的思想需要掌握,但是遇题还是要先判断清楚类型,选择相对简易方便、以及自己掌握熟练的算法。
代码二是在模糊地听完了DFS和BFS以后写出来的代码,也是我现在最能接受的简易代码,递归的运用关键在于准确找到前后的联系,递归的代码看上去简单,但是真的遇到新题,可就有的折腾了。这类题型中不管DFS还是BFS都有相似的check部分,即在走出下一步之前,要判断将要走的点,是否在给定范围之内(也有可能在自己的数组中越界),并有相关标记,表示此处来过,避免重复计数,防止递归或循环没有尽头。
代码三是书上的BFS算法,应该是标准的“模板了”,现在关于vector、queue、map之类的STL序列容器理解不深,掌握不全,运用不好,就算抄模板,也要多练习相关题目,熟悉此类题型的技巧规则。queue的.front() .pop() .push()时运用BFS解决题目的重要操作,queue是解决最短路径的利器,此处体现不多。
(勤加练习,勤写博客)2020/1/18/21:28
最新文章
- 程序猿是如何解决SQLServer占CPU100%的
- 学习笔记:Java的一些基础小知识之JVM与GC
- Android - ADB 的使用
- Javascript Function()中的降龙十八掌
- LOAD和PigStorage的一些测试例子 (转)
- Response.BinaryWrite()方法输出二进制图像
- dfs常见的配置文件中的value与description
- linux小包集合
- 《Head First 设计模式》ch.3 装饰(Decorator)模式
- UVA 10943 How do you add? DP
- Ubuntu14.04强化之conky——Harmattan主题
- Qt5 文本编辑
- C语言链表操作模板(添加,删除,遍历,排序)
- Asp.net MVC的Model Binder工作流程以及扩展方法(1)
- Docker - 从零开始到操作
- 【C#】时间类型修改
- android中如何获取指定目录下的图片
- USACO Section 2.1 The Castle 解题报告
- ZTree async中文乱码,ZTree reAsyncChildNodes中文乱码,zTree中文乱码
- [BUAA_SE_2017]代码复审-Week2