【搜索】多源BFS专题,存用特殊符号分隔的网格图的方法
学习资料
1.如何使用「多源 BFS」降低时间复杂度
2.【Leetcode】图的多源BFS详解
3.多源BFS 矩阵距离
4.算法讲解062【必备】宽度优先遍历及其扩展
标准多源BFS
求网格图被全部遍历所需轮数(层数)
994. 腐烂的橘子 - 力扣(LeetCode)
using PII = pair<int, int>;
class Solution {
public:
vector<vector<int>> g;
int n, m, res;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs()
{
queue<PII> q;
int cnt1 = 0;//记录新鲜橘子的个数
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (g[i][j] == 2) q.push({i, j});
else if (g[i][j] == 1) cnt1++;
if (cnt1 == 0) return 0;
while (q.size() && cnt1) //cnt1如果提前为0了那就不需要循环了,提前返回
{
int len = q.size();
while (len--)
{
auto [x, y] = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= m) continue;
if (g[a][b] != 1) continue;
cnt1--;
g[a][b] = 2;
q.push({a, b});
}
}
res++;
}
return cnt1 ? -1 : res;//cnt1不为0说明有新鲜橘子没腐烂,返回-1,为0就返回层数
}
int orangesRotting(vector<vector<int>>& grid) {
g = grid, n = grid.size(), m = grid[0].size();
return bfs();
}
};
宜居星球改造计划
样例1
输入
YES YES NO
NO NO NO
YES NO NO
输出
2
说明:
经过 2 个太阳日,完成宜居改造。
样例2
输入
YES NO NO NO
NO NO NO NO
NO NO NO NO
NO NO NO NO
输出
6
说明:
经过 6 个太阳日,可完成改造
样例3
输入
NO NA
输出
-1
说明:
无改造初始条件,无法进行改造
样例4
输入
YES NO NO YES
NO NO YES NO
NO YES NA NA
YES NO NA NO
输出
-1
说明:
-1 ,右下角的区域,被周边三个死亡区挡住,无法实现改造
C++代码
// Problem: #OD293. 宜居星球改造计划
// Contest: Hydro
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 多源BFS
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
using PII = pair<int, int>;
const int N = 10;
int n, m;
vector<vector<string>> g(N);
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs()
{
queue<PII> q;
int ycnt = 0; // 记录网格图中yes的个数
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (g[i][j] == "YES")
{
q.push({i, j});
}
}
}
int res = 0;
while (q.size())
{
int len = q.size();
while (len--)
{
auto [x, y] = q.front();
q.pop();
ycnt++;
if (ycnt == n * m) return res; // 全部变成yes,直接返回层数
for (int i = 0; i < 4; i++)
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= m) continue;
if (g[a][b] == "YES" || g[a][b] == "NA") continue;
g[a][b] = "YES";
q.push({a, b});
}
}
res++;
}
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (g[i][j] == "NO") return -1;
return res;
}
void get() //存网格图
{
string s;
while (getline(cin, s))
{
for (int i = 0; i < s.size(); i++)
{
if (isalpha(s[i]))
{
int j = i;
string word;
while (j < s.size() && isalpha(s[j])) word += s[j++];
g[n].push_back(word);
i = j;
}
}
n++;
}
m = g[0].size();
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
get();
cout << bfs();
return 0;
}
计算疫情扩散时间
样例1
输入
1,0,1,0,0,0,1,0,1
输出
2
说明:
1天以后,地图中仅剩余中心点未被感染; 2天以后,全部被感染
样例2
输入
1,1,1,1,1,1,1,1,1
输出
-1
说明:
全部都感染
C++代码
// Problem: #OD239. 计算疫情扩散时间
// Contest: Hydro
// Memory Limit: 256 MB
// Time Limit: 1000 ms
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
using PII = pair<int, int>;
const int N = 210;
int cnt, n;
vector<vector<int>> g(N); // 只能初始化一维,千万别初始化两维,不然读不对
void get() //把字符串形式的网格图存入二维矩阵
{
string str;
getline(cin, str);
for (char x : str)
if (x != ',') n++;
n = sqrt(n);
int k = 0;
for (char &x : str)
{
if (x != ',')
{
int val = x - '0';
g[k].push_back(val);
cnt++; //记录本行读了几个数了(从1开始)
if (cnt == n) //读完一行了
{
k++; // 读下一行
cnt = 0; // 计数器归0
}
}
}
}
int bfs()
{
int cnt1 = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (g[i][j] == 1) cnt1++;
}
}
if (cnt1 == n * n || cnt1 == 0) return -1;
queue<PII> q;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (g[i][j] == 1)
{
q.push({i, j});
}
}
}
int ycnt = 0, res = 0;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
while (q.size())
{
int len = q.size();
while (len--)
{
auto [x, y] = q.front();
q.pop();
ycnt++; // 感染区域+1
if (ycnt == n * n) return res; // 全部感染,返回层数
for (int i = 0; i < 4; i++)
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= n) continue;
if (g[a][b] == 1) continue;
g[a][b] = 1;
q.push({a, b});
}
}
res++; // 层数
}
return res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
get();
cout << bfs();
return 0;
}
debug版代码(方便输出矩阵状态)
// Problem: #OD239. 计算疫情扩散时间
// Contest: Hydro
// Memory Limit: 256 MB
// Time Limit: 1000 ms
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
using i64 = long long;
using PII = pair<int, int>;
const int N = 210;
int cnt, n;
int a[N * N];
vector<vector<int>> g(N); // 只能初始化一维,千万别初始化两维,不然读不对
void print()
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cout << g[i][j] << ' ';
}
cout << '\n';
}
}
void get()
{
string str;
getline(cin, str);
for (char x : str)
if (x != ',') n++;
n = sqrt(n);
int k = 0;
for (char &x : str)
{
if (x != ',')
{
int val = x - '0';
g[k].push_back(val);
cnt++;
if (cnt == n)
{
k++;
cnt = 0;
}
}
}
}
int bfs()
{
int cnt1 = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (g[i][j] == 1) cnt1++;
}
}
if (cnt1 == n * n || cnt1 == 0) return -1;
queue<PII> q;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (g[i][j] == 1)
{
q.push({i, j});
}
}
}
int ycnt = 0, res = 0;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
while (q.size())
{
int len = q.size();
while (len--)
{
auto [x, y] = q.front();
q.pop();
ycnt++; // 感染区域+1
if (ycnt == n * n) return res; // 全部感染,返回层数
for (int i = 0; i < 4; i++)
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= n) continue;
if (g[a][b] == 1) continue;
g[a][b] = 1;
q.push({a, b});
}
}
res++; // 层数
}
return res;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
get();
// print();
cout << bfs();
return 0;
}
有步数限制的多源BFS
跳马(多源BFS变种,每个起点有步数限制)
补充几个测试样例
输入
3 2
. .
2 .
. .
输出
0
输入
3 5
4 7 . 4 8
4 7 4 4 .
7 . . . .
输出
17
输入
3 4
. . . .
. 2 . .
. . . .
输出
0
输入
3 4
. . . .
. 2 2 .
. . . .
输出
-1
本题很坑爹的地方在于,输入的字符串还用空格分开,所以读入时不能用读连续字符串的方法,例如
读字符串数组(n * m
字符矩阵,中间无空格)char g[N][N]
写法
for (int i = 0; i < n; i++) scanf("%s", g[i]);//读取
for (int i = 0; i < n; i++) printf("%s\n", g[i]);//输出写法1
for (int i = 0; i < n; i++) puts(g[i]);//输出写法2
只能当成矩阵一个一个读,就和读int型二维矩阵一样。
分析:暴力法
本题数据量不大且因为每个点有移动步数限制,所以采用每个点都搜一次的方法,搜完一个点,dist数组中存的就是该点到其余所有位置的最小步数,更新到total数组中,如果dist[i][j] == -1
,或dist[i][j] > step
,则说明该点不可达,在total中标记为-1.
最后遍历一遍total数组,把所有-1
改成正无穷,再取最小值输出即可。
C++代码
/**
* Created by Tshaxz on 25-1-16.
*/
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 30, INF = 0x3f3f3f3f;
int n, m;
char g[N][N];
int dist[N][N], total[N][N];
int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int dy[8] = {2, 1, -1, -2, -2, -1, 1, 2};
void bfs(int x, int y)
{
memset(dist, -1, sizeof dist);
queue<PII> q;
dist[x][y] = 0;
q.push({x, y});
while (q.size())
{
auto [x, y] = q.front();
q.pop();
for (int i = 0; i < 8; i++)
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= m) continue;
if (dist[a][b] != -1) continue;
dist[a][b] = dist[x][y] + 1;
q.push({a, b});
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
cin >> g[i][j];
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (g[i][j] != '.')
{
int step = g[i][j] - '0';
bfs(i, j);
for (int x = 0; x < n; x++)
for (int y = 0; y < m; y++)
{
if (dist[x][y] > step) total[x][y] = -1;
else if (total[x][y] != -1) total[x][y] += dist[x][y];
}
}
int res = INF;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
if (total[i][j] == -1) total[i][j] = INF;
res = min(res, total[i][j]);
}
if (res == INF) res = -1;
cout << res << '\n';
return 0;
}
debug版代码,可以输出dist与total数组查看
/**
* Created by Tshaxz on 25-1-16.
*/
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 30, INF = 0x3f3f3f3f;
int n, m;
char g[N][N];
int dist[N][N], total[N][N];
int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int dy[8] = {2, 1, -1, -2, -2, -1, 1, 2};
void bfs(int x, int y)
{
memset(dist, -1, sizeof dist);
queue<PII> q;
dist[x][y] = 0;
q.push({x, y});
while (q.size())
{
auto [x, y] = q.front();
q.pop();
for (int i = 0; i < 8; i++)
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= m) continue;
if (dist[a][b] != -1) continue;
dist[a][b] = dist[x][y] + 1;
q.push({a, b});
}
}
}
// void print1()
// {
// printf("dist: \n");
// for (int i = 0; i < n; i++)
// {
// for (int j = 0; j < m; j++) printf("\t%d\t", dist[i][j]);
// puts("");
// }
// }
// void print2()
// {
// printf("total:\n");
// for (int i = 0; i < n; i++)
// {
// for (int j = 0; j < m; j++) printf("\t%d\t", total[i][j]);
// puts("");
// }
// }
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
cin >> g[i][j];
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (g[i][j] != '.')
{
int step = g[i][j] - '0';
bfs(i, j);
// printf("当前的马:g[%d][%d]:%c\n", i, j, g[i][j]);
// print1();
for (int x = 0; x < n; x++)
for (int y = 0; y < m; y++)
{
if (dist[x][y] > step) total[x][y] = -1;
else if (total[x][y] != -1) total[x][y] += dist[x][y];
}
// print2();
}
int res = INF;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
if (total[i][j] == -1) total[i][j] = INF;
res = min(res, total[i][j]);
}
if (res == INF) res = -1;
cout << res << '\n';
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析