DFS深度优先搜索 搜索用法: 对空间进行遍历
特点: 利用栈 stack
进行搜索,空间占用为 $O(h)$ (h指树与图的高度)。尽量往子节点搜索,搜到头时回溯,同时查找是否能够进入其他子节点
例题: 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 int path[N];bool st[N];void dfs (int 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:按行搜索
时间复杂度 $O(n!)$
按行搜索放皇后时,我们需要为该点所在列,正反对角线打标记表示该列,正反对角线被占用
为此,我们需要找到每个点所在位置的列,正反对角线的表示规律:
对于列,可用 col[i]
表示
对于正对角线,发现在同一条对角线上,横纵坐标相加的和总是相等的 ,因此我们可以使用 dg[u + i]
表示当前正对角线
对于反对角线,发现横纵坐标之差总是相等的 ,因此我们可以使用 udg[i - u]
来表示当前反对角线。此时问题出现了,$i$ 是可以小于 $u$ 的
数组下标写错,可是会 RE 得很惨
因此我们将所有的下标右移 $n$ ,从而保证数组下标恒正,因此要注意开两倍的数据范围 $N$
正解:
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,此算法按点枚举是否可行,因此需要逐一判断行 row
,列 col
,正对角线 dg
,反对角线 udg
是否冲突,并且时间复杂度也会起飞,达到 $O(2^{n^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 char g[N][N];bool row[N], col[N], dg[N], udg[N];void dfs (int x, int y, int s) { if (y == n) y = 0 , x ++; if (x == 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]) { 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];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); 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; int x = n - 1 , y = m - 1 ;while (x || y){ cout << x << ' ' << y << endl; x = Prev[x][y].first, y = Prev[x][y].second; }