搜索与图论 - DFS与BFS

深度优先搜索(DFS):

从某个节点往下开始搜索,走头,走到不能走的时候再回去(回溯)。

宽度优先搜索(BFS):

一层一层往下搜索,每次扩展一层。

数据结构 空间
DFS stack $O(h)$
BFS queue $O(2^h)$

BFS 有一个最短路的概念。

题目:排列数字

来源: https://www.acwing.com/problem/content/844/

给定一个整数n,将数字1~n排成一排,将会有很多种排列方法。

现在,请你按照字典序将所有的排列方法输出。

输入格式
共一行,包含一个整数n。

输出格式
按字典序输出所有排列方案,每个方案占一行。

数据范围

$ 1≤n≤7 $

输入样例:

1
3

输出样例:

1
2
3
4
5
6
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 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
26
27
28
29
30
31
32
33
#include <iostream>

using namespace std;

const int N = 10;
int path[N], n;
bool st[N];

void dfs(int u)
{
// 最后一位填完u + 1是 n
if (u == n)
{
for(int i = 0; i < n; i++) printf("%d ", path[i]);
puts("");
return;
}
for(int i = 1; i <= n; i++ )
if (!st[i])
{
path[u] = i;
st[u] = true;
dfs(u + 1);
st[i] = false;
}
}

int main()
{
cin >> n;
dfs(0);
return 0;
}

题目: n - 皇后问题

来源: https://www.acwing.com/problem/content/845/

n-皇后问题是指将 n 个皇后放在 n∗n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。

现在给定整数n,请你输出所有的满足条件的棋子摆法。

输入格式
共一行,包含整数n。

输出格式
每个解决方案占n行,每行输出一个长度为n的字符串,用来表示完整的棋盘状态。

其中”.”表示某一个位置的方格状态为空,”Q”表示某一个位置的方格上摆着皇后。

每个方案输出完成后,输出一个空行。

输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围

$ 1≤n≤9 $

输入样例:

1
4

输出样例:

1
2
3
4
5
6
7
8
9
.Q..
...Q
Q...
..Q.

..Q.
Q...
...Q
.Q..

思路:

dfs + 剪枝(提前判断,去掉不合法的示例)

搜索方式一: 按照每一个格子开始搜索

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>

using namespace std;
const int N = 10;
int n;
char g[N][N];
bool row[N], col[N], dg[N * 2], udg[N * 2];

// x, y 表示棋盘上的坐标,s表示皇后的数量
void dfs(int x, int y, int s)
{
// 来到每一行最后一个格子
if(y == n) x++, y = 0;

// 来到最后一列
if (x == n)
{
// 皇后的数量等于n 个
if (s == n)
{
for(int i = 0; i < n; i++) puts(g[i]);
puts("");
}
return;
}

// 不放皇后
dfs(x, y + 1, s);

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

int main()
{
cin >> n;

for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
g[i][j] = '.';

dfs(0, 0, 0);

return 0;
}

搜索方式二: 进行提前剪枝

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
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>

using namespace std;
const int N = 20;
// col 表示当前列有没有皇后
// dg 表示对角线有没有皇后
// udg 表示反对角线有没有皇后
bool col[N], dg[N], udg[N];
char g[N][N];
int n;

void dfs(int u)
{
// u 表示来到第n层
if (u == n)
{
for(int i = 0; i < n; i++) puts(g[i]);
puts("");
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[i + u] = udg[n - u + i] = true;
dfs(u + 1);
col[i] = dg[i + u] = udg[n - u + i] = false;
g[u][i] = '.';
}
}


int main()
{
cin >> n;

for(int i = 0; i< n; i++)
for(int j = 0; j < n; j++)
g[i][j] = '.';

dfs(0);
return 0;
}



题目: 走迷宫

来源: https://www.acwing.com/problem/content/846/

给定一个n*m的二维整数数组,用来表示一个迷宫,数组中只包含0或1,其中0表示可以走的路,1表示不可通过的墙壁。

最初,有一个人位于左上角(1, 1)处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。

请问,该人从左上角移动至右下角(n, m)处,至少需要移动多少次。

数据保证(1, 1)处和(n, m)处的数字为0,且一定至少存在一条通路。

输入格式
第一行包含两个整数n和m。

接下来n行,每行包含m个整数(0或1),表示完整的二维数组迷宫。

输出格式
输出一个整数,表示从左上角移动至右下角的最少移动次数。

数据范围

$ 1≤n,m≤100 $

输入样例:

1
2
3
4
5
6
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

输出样例:

1
8

思路:

bfs

bfs模板:

1
2
3
4
5
6
queue <- 初始值
while ( queue 不为空)
{
t <- 队头
扩展队头
}
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <cstring>
#include <queue>

using namespace std;
const int N = 110;

typedef pair<int, int> PII;
int n, m;
// g 表示图, d[x][y]表示个走到改点的最短距离
int g[N][N], d[N][N];

int bfs()
{
queue<PII> q;
memset(d, -1, sizeof d);
d[0][0] = 0;
q.push({0, 0});

int dx[4] = {0, 1, -1, 0}, dy[4] = {1, 0, 0, -1};
while (q.size())
{
auto t = q.front();
q.pop();

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 && d[x][y] == -1 && g[x][y] == 0)
{
q.push({x, y});
// x,y 点的距离等于上点到这的距离 + 1
d[x][y] = d[t.first][t.second] + 1;
}
}
}
return d[n - 1][m - 1];

}


int main()
{
cin >> n >> m;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
cin >> g[i][j];

cout << bfs() << endl;
return 0;
}