广度优先搜索(BFS)包含一下几个关键点:
1.状态
2.状态转移方式
3.有效状态
4.队列
5.标记
void bfs(起始点)
{
将起始点放入队列中;
标记起点访问;
while(如果队列不为空)
{
访问队列首元素X;
删除队首元素;
for(X 所有相邻点)
{
if(该点未被访问过且合法)
{
将该点加入队列末尾;
}
}
}
队列为空,广搜结束;
}
看一个例子:
说有一天公主被大魔王抓了,关进了一个迷宫里,需要你这位勇士去营救(当然成功了就自然是升职加薪赢取白富美啦),这个迷宫以二维数组的形式给出(暂定为5*5的迷宫)例:
[ 0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0 ]
其中0表示这里可以走,1表示这里是堵墙不能走,每次只能往上下左右四个方向走一个单位,迷宫入口为左上角,而公主在最右下角,问你是否能找到一条最短的路径成功救出公主,如果能输出其路径(每一个路径以坐标形式给出,如(0,0)),不能输出-1。
广度优先遍历其实是对状态的一种遍历,这里要搞清楚什么是状态。我们先想一下从第一个点出发,可以往右、下走,然后如果往右走,又会延伸出往右、下走,我们用一棵树来表示((1,2,3)表示x=1,y=2,路径长度也可以叫时间为3)
一个点加上一个时间t便构成了一个所谓的状态,而广度优先搜索就是按层,一层一层的搜索这些状态,直到找到要求的状态为止。
上图还有地方需要修改,因为第二次到达的点所需要的时间肯定比第一次到达所需要的时间多,如上图的(0,0,3),所以,我们设置一个标记,凡是我们到达过的点,便不进行第二次的搜索与延伸,这就是所谓的剪枝。这样一来状态的总数便等于A*B(A为总行数,B为总列数),只要这个时间复杂度在我们的接受范围之内,我们便可以采用广度优先搜索。
状态转移是指,由一个状态可以延伸出其他的那些状态,也就是可以往哪些地方走的问题。
说了这么多,我们看一下代码:
#include
#include
#include
using namespace std;
int migong[5][5];//保存迷宫
bool flag[5][5];//标记该点是否到达过
class Stat
{
public:
int x,y;
int t;
Stat * father;//指向其父状态,用于逆向寻找路径
};
int R[4][2] = {
{-1, 0},
{1, 0},
{0, -1},
{0, 1}
};//用于状态扩展
Stat* BFS()//返回终点状态
{
queue Q;//用于存储还没有被扩展的状态
int x = 0, y = 0, t = 0;
Stat* start = new Stat();
start->x = start->y = 0;
start->t = 0;
start->father = NULL;
Q.push(start);
while(!Q.empty())
{
Stat* temp = Q.front();
Q.pop();
for(int i = 0; i < 4; i++)//这里就是状态的扩展,向上下左右四个方向扩展
{
x = temp->x + R[i][0];
y = temp->y + R[i][1];
if(x < 0 || y < 0 || x > 4 || y > 4)//超出边界,便直接舍弃该状态
continue;
if(flag[x][y] == true)//到达过该状态,也直接舍弃
continue;
if(migong[x][y] == 1)//没有路也直接舍弃
continue;
Stat* temps = new Stat();
temps->x = x;
temps->y = y;
temps->t = temp->t+1;//时间+1
temps->father = temp;//指向父结点
if(x == 4 && y == 4)//如果搜索到了目标状态,便返回
return temps;
Q.push(temps);//将新状态加入队列
flag[x][y] = true;//标记该状态已经到达过
}
}
return start;
}
int main()
{
for(int i = 0; i < 5; i++)
for(int j = 0; j < 5; j++)
{
cin >>migong[i][j];
flag[i][j] = false;
}
Stat* p = BFS();
stack S;//放入栈中,主要是为了让其反序,不然从目标状态找其父节点遍历的话,是反的
while(p != NULL)
{
S.push(p);
p = p->father;
}
while(S.empty() == false)
{
Stat* temp = S.top();
S.pop();
cout << '(' << temp->x << ',' << temp->y << ')' << endl;
}
return 0;
}
相信大家看了代码也就理解的八九不离十了,这里说一下里面队列的作用,主要利用了队列的先进先出的特性,当一个状态向四个方向扩展时,依此将其放入队列中,再由这四个状态继续扩展,这样队列状态的取出便是按照层次的顺序,一层一层地遍历,这才是广度优先搜索的关键。
https://blog.csdn.net/qq_33552775/article/details/80689444?utm_source=blogxgwz2
挑战任务
“绿盟杯”决赛完美落幕之后,赛事团队组织去一个风景优美的山区进行团建。由于人数众多必须选择一块较大的场地。他们找到了一块足够大的地方,但是场地上却散布着许多石头,为了方便活动,必须把这些石头挪开。现在我们假设整个场地是一块矩形的地图,地图坐标的横纵坐标均为非负整数,每个坐标点上有一个值:
0:代表无法从这个点通过
1:代表这个点可以顺利通过
N(大于1):代表这个点上有一个可以去除的石头,而且石头的大小是N
现在要求你按照以下规则移除石头:从当前位置出发,按石头的大小依次移除石头,每次先移除从当前位置出发能够达到的最小的石头,每移除一个石头,该石头所在坐标就可以通行即值变为1
。你需要编写一个程序计算出从坐标(0,0)
出发,移除所有石头需要走的最小步数,注意,石头是无法翻越的,而且如果(0,0)上有石头可以直接移除。如果无法移除所有的石头就输出-1
。一个温馨的小提示仅供大家参考,大家注意哦:使用BFS算法再配以适合的数据结构如优先队列等是一个不错的选择!
编程要求
补全右侧代码区中的int getMiniumSteps(vector
函数,完成挑战任务中提出的要求:按照石头大小,从小到大依次移除场地中的石头,返回最小的步数,如果无法移除所有的石头就返回-1
。
函数参数说明如下:vector
:场地中各个坐标点的情况(vector[0][0]
代表坐标(0,0)
),即0
代表无法通过,1
代表可以顺利通过,大于1
的正数代表存在一个可以移除的石头,且石头的大小是该点的值。
测试说明
样例1:
输入:
1,2,3
0,0,4
7,6,5
输出:6
样例2:
输入:
1,2,3
0,0,0
7,6,5
输出:-1
#第三题
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class TeamBuilding {
public:
int getMiniumSteps(vector> forest) {
if (forest.empty() || forest[0].empty()) return 0;
int m = forest.size(), n = forest[0].size();
vector> trees;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (forest[i][j] > 1) trees.push_back({forest[i][j], i, j});
}
}
sort(trees.begin(), trees.end());
int ans = 0;
for (int i = 0, cur_row = 0, cur_col = 0; i < trees.size(); i++) {
int step = next_step(forest, cur_row, cur_col, trees[i][1], trees[i][2]);
if (step == -1) return -1;
ans += step;
cur_row = trees[i][1];
cur_col = trees[i][2];
}
return ans;
}
private:
int next_step(vector>& forest, int sr, int sc, int er, int ec) {
if (sr == er && sc == ec) return 0;
int m = forest.size(), n = forest[0].size();
queue> myq;
myq.push({sr, sc});
vector> visited(m, vector(n, 0));
visited[sr][sc] = 1;
int step = 0;
vector dir = {-1, 0, 1, 0, -1};
while (!myq.empty()) {
step++;
int sz = myq.size();
for (int i = 0; i < sz; i++) {
int row = myq.front().first, col = myq.front().second;
myq.pop();
for (int i = 0; i < 4; i++) {
int r = row + dir[i], c = col + dir[i+1];
if (r < 0 || r >= m || c < 0 || c >= n || visited[r][c] == 1 || forest[r][c] == 0) continue;
if (r == er && c == ec) return step;
visited[r][c] = 1;
myq.push({r, c});
}
}
}
return -1;
}
};