Algorithm8.1 BFS
[TOC]
bfs
[模版] 最多可到达
https://www.luogu.com.cn/problem/P1683
题目大意:最多可到达的格点数
#include <iostream>
using namespace std;
#define maxn 23
int n, m, dir[4][2]={{0, 1}, {0, -1}, {1, 0}, {-1, 0}}, ans=1, vis[maxn][maxn];
char mp[maxn][maxn];
#include <queue>
struct node{
int x, y;
node(int x,int y): x(x), y(y){}
};
queue<node> q;
void bfs(int x, int y) {
q.push(node(x, y));
vis[x][y] = 1;
while (!q.empty()) {
node nw = q.front();q.pop();
for (int i = 0; i < 4; ++i) {
int nx = nw.x+dir[i][0], ny = nw.y+dir[i][1];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && mp[nx][ny] == '.' && !vis[nx][ny]) {
vis[nx][ny] = 1;
q.push(node(nx, ny));
ans++;
}
}
}
printf("%d\n", ans);
}
int main() {
scanf("%d%d", &m, &n);
for (int i = 0; i < n; ++i) scanf("%s", mp[i]);
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (mp[i][j] == '@') bfs(i, j);
return 0;
}
最少步数
https://www.luogu.com.cn/problem/P1746
题目大意:从地图上的起点到终点所需要的最少步数
1、在模版的基础上,对结构体(也就是node)增加一个属性val,用来记录蔓延到当前的点所需要的步数。
2、还是要去维护一个vis数组来避免重复猜,虽然最短步数=>到了终点立马跳出,但是如果一直到不了的话,不加vis数组就永远跳不出。
#include <iostream>
using namespace std;
#define maxn 1005
int n, bgx, bgy, edx, edy, dir[4][2]={{0, 1}, {0, -1}, {1, 0}, {-1, 0}}, vis[maxn][maxn];
char mp[maxn][maxn];
#include <queue>
struct node{
int x, y, val;
node(int x, int y, int val): x(x), y(y), val(val){}
};
queue<node> q;
void bfs(int x, int y) {
q.push(node(x, y, 0));
vis[x][y] = 1;
while (!q.empty()) {
node nw = q.front();q.pop();
if (nw.x == edx-1 && nw.y == edy-1) {printf("%d\n", nw.val);return;}
for (int i = 0; i < 4; ++i) {
int nx = nw.x+dir[i][0], ny = nw.y+dir[i][1];
if (nx >= 0 && nx < n && ny >= 0 && ny < n && mp[nx][ny] == '0' && !vis[nx][ny]) {
vis[nx][ny] = 1;
q.push(node(nx, ny, nw.val+1));
}
}
}
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++i) scanf("%s", mp[i]);
scanf("%d%d%d%d", &bgx, &bgy, &edx, &edy);
bfs(bgx-1, bgy-1);
return 0;
}
最短路径
(同时也可以顺手算上最短步数)
http://poj.org/problem?id=3984(未ac)
题目大意:给出一个01迷宫,求起到终点的路径
1、记录路径的思路:通过在结构体中,定义一个指向其他节点的指针,指向者就是从谁哪里来到的这个点。
2、在绑定每一层的指向关系的时候,直接通过队列拿出来的&q.front()
去指向,而不是先node nw = q.front();
赋值,然后再把&nw
去赋值
3、在最后搜索完成之后,从终点 顺着每个node的fa指针,找到是哪个节点到这个节点的,然后一步一步找回去,最后再逆序输出即可。
#include <iostream>
using namespace std;
#define maxn 20
int mp[maxn][maxn], vis[maxn][maxn], dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}, ansx[300], ansy[300], idx;
struct node{
int x, y;
node * fa;
node(){}
node(int x, int y, node * fa): x(x), y(y), fa(fa){}
};
#include <queue>
queue<node> q;
void printTrace(node nw){
ansx[idx] = nw.x;ansy[idx++] = nw.y;
node * nt = nw.fa;
while (nt!=NULL) {
ansx[idx] = nt->x;ansy[idx++] = nt->y;
nt = nt->fa;
}
for (int i = idx-1; i >= 0; --i) printf("(%d, %d)\n",ansx[i], ansy[i]);
}
void bfs(int x, int y) {
q.push(node(x, y, NULL));
while (!q.empty()) {
node nw = q.front();
if (nw.x == 4 && nw.y == 4) {printTrace(nw);return;}
for (int i = 0; i < 4; ++i) {
int nx = nw.x+dir[i][0], ny = nw.y+dir[i][1];
if (nx == 4 && ny == 4) {printTrace(node(nx, ny, &nw));return;}
if (nx >= 0 && nx < 5 && ny >= 0 && ny < 5 && !mp[nx][ny] && !vis[nx][ny]) {
vis[nx][ny] = 1;
q.push(node(nx, ny, &q.front()));
}
}
q.pop();
}
}
int main() {
for (int i = 0; i < 5; ++i)
for (int j = 0; j < 5; ++j)
scanf("%d", &mp[i][j]);
bfs(0,0);
return 0;
}
AC代码:
通过在节点中添加一个属性pre
,用来表示这个节点的前继节点是谁。
易错:在创建bfs的数据结构queue的时候,队列中的数据类型应该是指向结构体的指针,所以应该表示为queue<node *> q;
区分结构体与指向结构体的指针,在写代码的过程中,可以发现指向结构体的指针在调用其属性时是通过->
进行调用,而结构体是通过.
进行调用
#include <iostream>
using namespace std;
int mp[6][6], dir[4][2]={{0, 1}, {0, -1}, {1, 0}, {-1, 0}}, vis[6][6];
struct node {
int x, y;
node * pre;
};
#include <vector>
vector<pair<int, int>> b;
void printtrack(node * nw) {
while (nw) {
b.push_back(make_pair(nw->x, nw->y));
nw = nw->pre;
}
for (int i = b.size()-1; i >= 0; --i) printf("(%d, %d)\n", b[i].first-1, b[i].second-1);
}
#include <queue>
queue<node *> q;
void bfs(){
vis[1][1] = 1;
node * tmnode = new node;
tmnode->x = 1, tmnode->y = 1, tmnode->pre = NULL;
q.push(tmnode);
while (!q.empty()) {
node * nw = q.front();q.pop();
if (nw->x == 5 && nw->y == 5) {printtrack(nw);return;}
for (int i = 0; i < 4; ++i) {
int nx = nw->x+dir[i][0], ny = nw->y+dir[i][1];
if (!vis[nx][ny] && !mp[nx][ny] && nx >= 1 && nx <= 5 && ny >= 1 && ny <= 5) {
node * ntnode = new node();
ntnode->x = nx, ntnode->y = ny;ntnode->pre = nw;
vis[nx][ny] = 1; q.push(ntnode);
}
}
}
}
int main() {
for (int i = 1; i <= 5; ++i)
for (int j = 1; j <= 5; ++j)
scanf("%d", &mp[i][j]);
bfs();
return 0;
}
[基本]联通块数
https://www.luogu.com.cn/problem/P1596
bfs实现 // dfs也可以实现,只不过是不同的搜索策略
#include <iostream>
using namespace std;
#define maxn 105
int n, m, cnt, dir[8][2] = {{1, 0}, {-1, 0}, {1, 1}, {-1, 1}, {1, -1}, {-1, -1}, {0, 1}, {0, -1}};
char a[maxn][maxn];
struct node{
int x, y;
node(int x, int y): x(x), y(y){}
};
#include <queue>
queue<node>q;
void bfs(int x, int y) {
a[x][y] = '.';
q.push(node(x, y));
while (!q.empty()) {
node nw = q.front();q.pop();
for (int i = 0; i < 8; ++i) {
int nx = nw.x + dir[i][0], ny = nw.y+dir[i][1];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && a[nx][ny] == 'W') {a[nx][ny] = '.';q.push(node(nx, ny));}
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) scanf("%s", a[i]);
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (a[i][j] == 'W') {cnt++;bfs(i, j);}
printf("%d\n", cnt);
return 0;
}
联通块数 + 染色
01迷宫-可到达的方块总数:https://www.luogu.com.cn/problem/P1141 (未ac)
题目大意:一个地图上标有0和1的迷宫,移动规则为仅能从0移动到1或者从1移动到0。问最多可以移动到多少个地方。
1、移动规则的约束:通过异或来判断。mp[nwn.x][nwn.y]^mp[nx][ny]
,异或结果为1,那就是满足移动前和移动完之后的那两个位置不同,满足01或者10。
2、时间限制:由于会多次查询,时间会爆掉,所以通过联通块染色。染色方式有两种:
- 同种的直接给标记上这种的总个数。=>不能在bfs的过程中就标记上,要在过程中用容器给储存起来,等彻底延伸完了再重新遍历一边容器然后染色。
- 用一个自增数组,然后在遍历的过程中把索引标记给每个点,最后再利用索引去自增数组中找答案。
#include <iostream>
using namespace std;
struct node{
int x, y;
node(int x, int y):x(x), y(y){}
};
#include <queue>
queue<node> q;
#define maxn 1200
int n, m, tx, ty, mp[maxn][maxn], ans = 0, dir[4][2]={{0, 1}, {0, -1}, {1, 0}, {-1, 0}}, vis[maxn][maxn], block[maxn][maxn] ,cnt[1000100], idx = 1;
char s[maxn];
#include <cstring>
#define cl(a, b) memset(a, b, sizeof(a))
#include <vector>
//vector<node> kp;
void bfs(int x, int y){
while (!q.empty()){
node nwn = q.front();q.pop();
for (int i = 0; i < 4; ++i) {
int nx = nwn.x+dir[i][0], ny = nwn.y+dir[i][1];
if (nx > 0 && nx <= n && ny > 0 && ny <= n && !vis[nx][ny] && mp[nwn.x][nwn.y]^mp[nx][ny]){
ans++;
vis[nx][ny] = 1;
block[nx][ny] = idx;
q.push(node(nx, ny));
// kp.push_back(node(nx, ny));
}
}
}
// 染色
// for (int i = 0; i < kp.size(); ++i) block[kp[i].x][kp[i].y] = ans;
cnt[idx++] = ans;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) {
scanf("%s", s);
for (int j = 0; j < n; ++j) {
if (s[j] == '0') mp[i+1][j+1] = 0;
else mp[i+1][j+1] = 1;
}
}
while (m--) {
ans = 1;cl(vis, 0);
scanf("%d%d", &tx, &ty);
if (block[tx][ty]) {printf("%d\n", cnt[block[tx][ty]]);continue;}
vis[tx][ty] = 1;
block[tx][ty] = idx;
q.push(node(tx, ty));
// kp.push_back(node(tx, ty));
bfs(tx, ty);
printf("%d\n", ans);
}
return 0;
}