banner
NEWS LETTER

深度优先搜索与广度优先搜索 | OI笔记

Scroll down

DFS深度优先搜索

搜索用法:

对空间进行遍历

特点:

利用栈 stack 进行搜索,空间占用为 O(h) (h指树与图的高度)。尽量往子节点搜索,搜到头时回溯,同时查找是否能够进入其他子节点

tujie

例题:

acwing842-排列数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int path[N];
// 用于存储搜索到的方案
bool st[N];
// 用于存储当前的搜索情况中某个数是否被用过

void dfs(int u)
// u 表示当前搜索进行到的层数
{
if (u == n)
// 如果填完了数,即搜索到最后一个子节点
{
for (int i = 0; i < n; i ++)
cout << path[i] << " ";
// 对匹配成功的情况进行输出
cout << endl;
return;
// 结束
}

for (int i = 1; i <= n; i ++)
if (!st[i])
// 括号内是搜索成功的条件,视题目而定
{
path[u] = i;
st[i] = true;
// 存储这一层的结果
dfs(u + 1);
// 进行下一层搜索
st[i] = false;
// 恢复当前搜索的状态,回溯

}
}

剪枝的典型例题:

USACO1.5八皇后

解法1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 注:此版本基于行进行搜索,也就是提前保证每行只有一个皇后
char g[N][N];
bool col[N], dg[N], udg[N];
// 分别表示:列,正对角线(左下到右上),反对角线
void dfs(int u)
{
if (u == n)
{
for (int i = 0; i < n; i ++)
puts(g[i]);
cout << endl;
return;
}

for (int i = 0; i < n; i ++)
if (!col[i] && !dg[u + i] && !udg[n - u + i])
{
g[u][i] = 'Q';
col[i] = dg[u + i] = udg[n - u + i] = true;
dfs(u + 1);
col[i] = dg[u + i] = udg[n - u + i] = false;
g[u][i] = '.';
}
}

解法2;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 注:此版本基于点进行搜索,对于每个点都搜索一次,时间复杂度高
char g[N][N];
bool row[N], col[N], dg[N], udg[N];
// 分别表示:列,正对角线(左下到右上),反对角线
void dfs(int x, int y, int s)
// 分别表示 当前点的x坐标,当前点的y坐标,放到第几个皇后力
{
if (y == n) y = 0, x ++;
// 超出边界后换行
if (x == n)
// 图中无位置可放
{
if (s == n)
// 皇后满足条件
{
for (int i = 0; i < n; i ++)
puts(g[i]);
puts("");
}

return;
// 如果满足,那么输出答案后结束;如果不满足,那么直接结束
}

// 分类1:当前点不放皇后
dfs(x, y + 1, s);

// 分类2:当前点放皇后
if (!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n])
{
g[x][y] = 'Q';
row[x] = col[y] = dg[x + y] = udg[x - y + n] = true;
dfs(x, y + 1, s + 1);
row[x] = col[y] = dg[x + y] = udg[x - y + n] = false;
g[x][y] = '.';
}
}

BFS宽度优先搜索

特点:利用队列 queue 进行搜索,空间占用为 O(2 ^h^)。同时搜索父节点下的每一个子节点,逐层递进。BFS搜索的特点是 具有最短性(即到从父节点到子节点的最短路最先搜到) 注意BFS解最短路时只适用于每条路权重相同的情况

csdn - 宽度优先搜索图解

例题:

acwing844-走迷宫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
typedef pair<int, int> PII;
int map[N][N], d[N][N];
// map存储地图,d存储每一个点到起点的距离
PII q[N * N];
// 模拟队列
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
// 位置偏移向量

int bfs()
{
int hh = 0, tt = 0;
q[0] = {0, 0};
// 队列初始化
memset(d, -1, sizeof d);
// 初始化为 -1 的点表示没有走过
d[0][0] = 0;
// 从左上角开始宽搜

while (hh <= tt)
// 队列不空
{
auto t = q[hh ++];

for (int i = 0; i < 4; i ++)
{
int x = t.first + dx[i], y = t.second + dy[i];
// 位置偏移尝试
if (x >= 0 && x < n && y >= 0 && y < m && !map[x][y] && d[x][y] == -1)
// 合法条件:未出界,可以走,未走过
{
d[x][y] = d[t.first][t.second] + 1;
q[ ++ tt ] = {x, y};
}
}
}

return d[n - 1][m - 1];
}

若要存储路线:

1
2
3
4
5
6
7
8
9
10
11
12
13
PII Prev[N][N];
// 用于存储当前点是从哪里走来的
Prev[x][y] = t;
// 在159行添加,存储当前点的上一个点

int x = n - 1, y = m - 1;
while (x || y)
// x == y == 0表示到达起点
{
cout << x << ' ' << y << endl;
x = Prev[x][y].first, y = Prev[x][y].second;
}
// 在163行添加,倒序输出路径

树与图的存储

Other Articles
cover
KMP字符串匹配算法 | OI笔记
  • 22/10/14
  • 18:34
  • 信息竞赛
cover
基础动态规划 | OI笔记
  • 22/10/13
  • 13:40
  • 信息竞赛
Please enter keywords to search