目录
前言
深度优先搜索(Depth-First Search,DFS)和广度优先搜索(Breadth-First-Search,BFS)是基本的暴力技术,常用于解决图、树的遍历问题。
以小鼠走迷宫为例:
BFS的思路:一群老鼠走迷宫。假设老鼠是无限多的,这群老鼠进去后,在每个路口派出部分老鼠探索所有没走过的路。走某条路的老鼠,如果碰壁无法前行,就停下;如果到达的路口已经有其他老鼠探索过了,也停下。很显然,所有的道路都会走到,而且不会重复。这个思路就是BFS。 【一个点往外扩散出去】。BFS看起来像“并行计算”,不过,由于程序是单机顺序运行的,所以可以把BFS 看成是并行计算的模拟。
在具体编程时,一般用队列这种数据结构来具体实现 BFS。
用队列来进行BFS
- 1进队。当前队列是{1}
- 1出队,1的邻居2、3进队。当前队列是{2、3}【即:1扩散到2、3】
- 2出队,2的邻居4、5、6入队。当前队列是{3,4,5,6}
- 3出队,邻居7、8入队。
- 如此循环下去
- 直到队列为空
Red and Black
在一个瓷砖为红色或黑色的房间内,一个人站在黑色瓷砖上,他可以按上、下、左、右方向移动到相邻的瓷砖上,但是不能在红色瓷砖上移动,计算他可以到达的黑色瓷砖数量。
代码:
#include <bits/stdc++.h>
using namespace std;
char room[23][23];
int dir[4][2] = { {-1,0},//向左、左上角的坐标是{0,0}
{0,-1}, //向上
{1,0}, //向右
{0,1} //向下
};
int Wx, Hy, sum;//Wx行,Hy列,sum统计可走的位置
bool check(int x, int y)
{
return (x < Wx&& x >= 0 && y >= 0 && y < Hy);//在区域内
}
struct node {//坐标
int x, y;
};
void BFS(int dx, int dy) {
sum = 1;//起点,包含在内
queue<node>q;
node start, next;
start.x = dx;
start.y = dy;
q.push(start);
cout << start.x << ' ' << start.y << endl;
while (!q.empty()) {//队列为空退出
start = q.front();
q.pop();
cout << "pop : " << start.x << ' ' << start.y << endl;//打印队列情况
for (int i = 0; i < 4; i++) {//按左、上、右、下4个方向顺时针逐一搜索
next.x = start.x + dir[i][0];
next.y = start.y + dir[i][1];
if (check(next.x, next.y) && room[next.x][next.y] == '.') {//在区域内可走的地
room[next.x][next.y] = '#';//进队之后标记处理过的
sum++;
q.push(next);
}
}
}
cout << sum << endl;
}
int main()
{
int x, y, dx = 0, dy = 0;
while (cin >> Wx >> Hy)//Wx行 Hy列
{
if (Wx == 0 && Hy == 0)
break;//结束
for (x = 0; x < Wx; x++) { //Wx行
for (y = 0; y < Hy; y++) { //Hy列
cin >> room[x][y];
if (room[x][y] == '@') {//读入起点
dx = x;
dy = y;
}
}
}
BFS(dx, dy);
}
return 0;
}
/*
4 5
. . . . #
. . . . .
# @ . . .
. # . . #
15
*/
用BFS求最短路径:
最短路径是图论的一个基本问题,用BFS也是很好的最短路径算法。
方法:用BFS搜索所有点,记录到达每个点时经过的步数,即可得到从出发点到所有点的最短距离。
BFS 所最短路径的计算复杂度是O(V+E)
八数码问题
BFS搜索处理的对象不仅可以是一个数,还可以是一种“状态”。八数码问题是典型的状态图搜索问题。
“康托展开”——判重
状态 | 012345678 | 012345687 | 012345768 | 012345786 | ... | 876543210 |
Cantor | 0 | 1 | 2 | 3 | ... | 362880-1 |
第一行是0~8这9个数字的全排列,共9!=362880个,从小到达排序。第二行是每个排列对应的位置。
函数Cantor()实现的功能是:输入一个排列,即第1行的某个排列,计算出它的Cantor值,即第2行对应的数。
Cantor()的复杂度为O(n^2),n是集合中元素的个数。
在3x3的棋盘上放置编号为1—8的方块,每个占一格,另外还有一个空格。与空格相邻的数字方块可以移动到空格里。任务1:指定初始棋局和目标棋局,计算出最少的移动步数;任务2:输出数码的移动序列
把一个棋局看成一个状态图,总共有9!=362880个状态。从初始棋局开始,每次移动转到下一个状态,到达目标棋局后停止。
输入:1 2 3 0 8 4 7 6 5
1 0 3 8 2 4 7 6 5
2 | 0 | 3 |
1 | 8 | 4 |
7 | 6 | 5 |
1 | 0 | 3 |
8 | 2 | 4 |
7 | 6 | 5 |
八数码问题的搜索树:
思路:
1)A[1,0]进队,当前队列是{A};
2)A出队,A的邻居B[0,1]、C[1,1]、D[2,0]进队,当前队列是{B、C、D};步数为1
2)B出队,E[0,2]入队,当前队列是{C、D、E},E的步数为2;
4)C出队,转移到F[,检验F是目标状态,停止,输出F的步数2;
代码:
#include <bits/stdc++.h>
using namespace std;
const int LEN = 362880;//状态共362880种
struct nodes {
int state[9];//记录一个八数码的排列,即一个状态
int dis;//记录到起点的距离
};
int direction[4][2] = { {-1,0},{0,-1},{1,0},{0,1} };//左、上、右、下顺时针方向
int visited[LEN] = { 0 };//与每个状态对应的记录,Cantor()函数对它置数,并判重
int start[9];//开始状态
int goal[9];//目标状态
//Cantor()用到的常数
long int factory[] = { 1,1,2,6,24,120,720,5040,40320,362880 };
bool Cantor(int str[], int n) {//用康托展开判重
long result = 0;
for (int i = 0; i < n; i++) {
int counted = 0;
for (int j = i + 1; j < n; j++) {
if (str[i] > str[j])
++counted;//当前为未出现的元素排在第几个
}
result += counted * factory[n - i - 1];
}
if (!visited[result]) {//没有被访问过
visited[result] = 1;
return true;
}
return false;
}
int bfs() {
nodes head;
memcpy(head.state, start, sizeof(head.state));//复制起点的状态
head.dis = 0;
queue<nodes>q;
Cantor(head.state, 9);//用康托展开判重,目的是对起点的visited[]赋初值
q.push(head);//起点状态入栈
while (!q.empty()) {
head = q.front();
q.pop();
int z;
for (z = 0; z < 9; z++)//找该状态中元素0的位置
if (head.state[z] == 0)
break;
int x = z % 3;//横坐标
int y = z / 3;//纵坐标
for (int i = 0; i < 4; i++) {//转为二进制状态
int newx = x + direction[i][0];
int newy = y + direction[i][1];
int nz = newx + 3 * newy;//转化为一维
if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3) {
nodes newnode;
memcpy(&newnode, &head, sizeof(struct nodes));//复制新的状态
swap(newnode.state[z], newnode.state[nz]);//把0移到新的位置
newnode.dis++;
if (memcmp(newnode.state, goal, sizeof(goal)) == 0)//与目标状态对比
return newnode.dis;//返回距离
if (Cantor(newnode.state, 9))//判重 新状态放入队列
q.push(newnode);
}
}
}
return -1;//没找到
}
int main()
{
for (int i = 0; i < 9; i++)
cin >> start[i];//初始状态
for (int i = 0; i < 9; i++)
cin >> goal[i];//目标状态
int num = bfs();
if (num != -1)
cout << num << endl;
else
cout << "Impossible" << endl;
return 0;
}
/*
1 2 3 0 8 4 7 6 5
1 0 3 8 2 4 7 6 5
输出:2
*/