DFS
DFS:深度优先搜索,不断向深层搜索,搜索至叶子节点后返回.
DFS基础应用:
- DFS 连通性模型
- DFS 搜索顺序
- DFS 剪枝与优化
- 迭代加深
- 双向DFS
- IDA*
关于是否回溯:
- 对于图内的点的搜索,需要对每个点全部遍历一遍时,不需要回溯.
- 对于图的不同状态进行搜索时,每搜索一个状态结束后,需要回溯恢复现场,以便对整个图下一个状态进行搜索.
DFS 连通性模型:
AcWing 1112. 迷宫 原题链接
算法思路:
求解图中点的连通性,这里所有点需要遍历一遍,不需要回溯.
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 110;
int k, sx,sy,ex,ey,n;
char g[N][N];
int dx[4] = {1,0,-1,0};
int dy[4] = {0,-1,0,1};
int st[N][N];
int dfs(int x, int y)
{
if (g[x][y] == '#')
return 0;
if (x == ex && ey == y)
return 1;
st[x][y] = 1;
for (int i = 0; i < 4; i ++ ){
int xx = x + dx[i];
int yy = y + dy[i];
if (xx >= n || xx < 0 || yy >= n || yy < 0)
continue;
if (st[xx][yy])
continue;
if (dfs(xx,yy))
return 1;
}
return 0;
}
int main()
{
cin >> k;
while (k -- ){
cin >> n;
char c = getchar();
for (int i = 0; i < n; i ++ )
scanf("%s",g[i]);
memset(st, 0, sizeof st);
cin >> sx >> sy >> ex >> ey;
if (dfs(sx, sy))
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
AcWing 1113. 红与黑 原题链接
算法思路 :
求连通块内点的数量,DFS不需要回溯.
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 30;
char g[N][N];
int st[N][N];
int n, m;
int dx[4] = {1,0,-1,0};
int dy[4] = {0,-1,0,1};
int dfs(int x, int y)
{
int cnt = 1;
st[x][y] = 1;
for (int i = 0; i < 4; i ++ ){
int xx = dx[i] + x;
int yy = dy[i] + y;
if (xx > n || xx < 1 || yy > m || yy < 1)
continue;
if (st[xx][yy])
continue;
if (g[xx][yy] != '.')
continue;
cnt += dfs(xx,yy);
}
return cnt;
}
int main()
{
while (cin >> m >> n && n && m){
char c = getchar();
for (int i = 1; i <= n; i ++ )
scanf("%s",g[i] + 1);
int x, y;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
if (g[i][j] == '@'){
x = i;
y = j;
}
memset(st, 0, sizeof st);
cout << dfs(x, y) << endl;
}
return 0;
}
DFS 搜索顺序
AcWing 1116. 马走日 原题链接
算法思路 :
题目需要求解从起点出发有多少中方法可以走完全图, 对图的状态进行搜索, 每次向下搜索一次, 马走过的点多一个, 当下一步不再合法或是搜完全图后, 需要进行回溯恢复现场,以便对下一个状态继续搜索.
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 10;
int st[N][N];
int t;
int res;
int dx[8] = {-2, -1, 1, 2, 2, 1,-1,-2};
int dy[8] = { 1, 2, 2, 1,-1,-2,-2,-1};
int n,m;
void dfs(int x, int y, int cnt)
{
if (cnt == n * m){
res ++;
return;
}
st[x][y] = 1;
for (int i = 0; i < 8; i ++ ){
int xx = x + dx[i];
int yy = y + dy[i];
if (xx < 0 || xx >= n || yy < 0 || yy >= m)
continue;
if (st[xx][yy])
continue;
dfs(xx, yy, cnt + 1);
}
st[x][y] = 0;
}
int main()
{
cin >> t;
while (t -- ){
int x,y;
cin >> n >> m >> x >> y;
memset (st, 0, sizeof st);
res = 0;
dfs(x,y,1);
cout << res << endl;
}
return 0;
}
AcWing 1117. 单词接龙 原题链接
算法思路 :
从起始单词开始向下搜索,每次遍历可以向后添加的单词.注意回溯.
注意求一个单词后缀与另一个单词的前缀的最小重合字母数量.
#include <iostream>
#include <cstring>
#include <queue>
#include <string>
using namespace std;
const int N = 25;
int n;
string words[N];
int g[N][N]; //g[i][j]表示i号单词与j号单词最小公共长度
int sum[N]; //存储每个单词出现的次数
int ans = 0;
void dfs(string state, int x)
{
ans = max((int)state.size(), ans);
//cout << state << endl;
sum[x] ++;
for (int i = 1; i <= n; i ++ )
if (sum[i] < 2 && g[x][i]){
dfs(state + words[i].substr(g[x][i]), i);
}
sum[x] --;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ )
cin >> words[i];
char start;
cin >> start;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ ){
for (int k = 1; k < words[i].size() && k < words[j].size(); k ++ ) //注意长度至少为1
if (words[i].substr(words[i].size() - k, k) == words[j].substr(0,k)){
g[i][j] = k;
//cout << g[i][j] << ' ';
break;
}
}
for (int i = 1; i <= n; i ++ )
if (words[i][0] == start)
dfs(words[i], i);
cout << ans << endl;
return 0;
}
AcWing 1118. 分成互质组 原题链接
- (代填)
DFS之剪枝与优化
常用剪枝方法:
-
优化搜索顺序: 优先搜索分支较少的点
-
排除等效冗余: 不搜索等效的状态(一般是将排列转化为组合)
-
可行性剪枝: 不合法时及时停止
-
最优性剪枝: 确定该方案不是最优解时及时停止
AcWing 165. 小猫爬山 原题链接
算法思路:
这里对根据车的数量进行分支, 对每一只猫, 遍历每一辆车, 找到其中一辆可以加入的车, 当无法加入任何一辆车时, 新开一辆车, 直到所有猫都有车可坐,所以每只猫会有向下的$k$(当前车辆数) + 1 个分支
三点优化:
1. 优化搜索顺序: 先加入胖猫,由于分支相同时,胖猫的合法分支较少.
2. 最优性剪枝
3. 可行性剪枝
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20;
int cars[N];
int n, m;
int cats[N];
int ans = N;
void dfs(int u, int k)
{
//最优性剪枝
if (k >= ans)
return;
if (u > n){
ans = k;
return ;
}
for (int i = 1; i <= k; i ++ )
if (cars[i] + cats[u] <= m){
cars[i] += cats[u];
dfs(u + 1, k);
cars[i] -= cats[u];
}
cars[k + 1] = cats[u];
dfs(u + 1, k + 1);
cars[k + 1] = 0;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
cin >> cats[i];
//优化搜索顺序:
sort(cats + 1, cats + n + 1);
reverse(cats + 1, cats + n + 1);
dfs(1, 1);
cout << ans << endl;
return 0;
}
强!