1.dfs为深度搜索算法
可以在脑海中想像一颗树, 从根节点开始选择左子节点或右子节点向下并且标记,直到到达叶子结点。 回溯到上一层,看是否存在未被标记的节点,然后向下走。
应用场景: 1.排列组合问题 2.N * N 走迷宫问题 3.连通块
实战分析:
1.排列问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15;
int n;
int path[N];
bool st[N];
void dfs(int pos)
{
if(pos > n)
{
for(int i = 1; i <= n; i++)
{
cout<<path[i]<<" ";
}
cout<<endl;
return ;
}
for(int i = 1; i <= n; i++)
{
if(!st[i])
{
path[pos] = i;
st[i] = true;
dfs(pos + 1);
st[i] = false;//恢复现场
}
}
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
2.组合问题
分析问题:
题目可以抽象为从1~n中选数,存在一种选 / 不选的问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20;
int n;
int path[N];
bool st[N];
void dfs(int pos)
{
if(pos > n)
{
for(int i = 1; i <= n; i ++)
{
if(st[i]) cout<<i<<" ";
}
cout<<endl;
return ;
}
//不选
dfs(pos + 1);
//选
st[pos] = true;
dfs(pos + 1);
st[pos] = false; //恢复现场
}
int main()
{
cin>>n;
dfs(1); //个人习惯pos从1开始
return 0;
}
3.走迷宫问题(是否可达/遍历)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 30;
int n, m; //行列
char g[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dfs(int sx, int sy)
{
int cnt = 1;
g[sx][sy] = '#';
for(int i = 0; i < 4; i++)
{
int x = sx + dx[i], y = sy + dy[i];
if(x < 0 || x >= n || y < 0 || y >= m) continue;
if(g[x][y] != '.') continue;
cnt += dfs(x, y);
}
return cnt;
}
int main()
{
while(cin>>m>>n, n || m)
{
for(int i = 0; i < n; i++) cin>>g[i];
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
{
if(g[i][j] == '@')
cout<<dfs(i, j)<<endl;
}
}
return 0;
}
4.连通块(求连通块个数或大小)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000 + 10;
int n;
char g[N][N];
bool st[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
void dfs(int sx, int sy, int &total, int &bound)
{
st[sx][sy] = true;
total ++;//连通块个数
bool is_bound = false;
for(int i = 0; i < 4; i++)
{
int x = sx + dx[i], y = sy + dy[i];
if(x < 0 || x >= n || y < 0 || y >= n || st[x][y]) continue;
if(g[x][y] == '.') //表示沿海
{
is_bound = true;
continue;
}
dfs(x, y, total, bound);
}
if(is_bound) bound++;
}
int main()
{
cin>>n;
for(int i = 0; i < n; i++) cin>>g[i];
int ans = 0;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
{
if(g[i][j] == '#' && !st[i][j])
{
int total = 0, bound = 0;
dfs(i, j, total, bound);
if(total == bound) ans ++;
}
}
cout<<ans<<endl;
return 0;
}
2.bfs为宽度搜索算法
可以在脑海中想像一棵树,从根节点开始选择下一层所有可达且未被标记的点加入队列中,一层一层的遍历下去,所以也称为层序遍历。
应用场景:
1.最短距离问题
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 100 + 10;
int n, m;
int g[N][N];
int dist[N][N];
queue<PII> q;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs()
{
memset(dist, -1, sizeof dist);
q.push({0, 0});
dist[0][0] = 0;
while(q.size())
{
auto t = q.front();
q.pop();
if(t.x == n - 1 && t.y == m - 1) return dist[n - 1][m - 1];
for(int i = 0; i < 4; i++)
{
int a = t.x + dx[i], b = t.y + dy[i];
if(a < 0 || a >= n || b < 0 || b >= m) continue;
if(g[a][b] == 1 || dist[a][b] != -1) continue;
dist[a][b] = dist[t.x][t.y] + 1;
q.push({a, b});
}
}
return dist[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;
}
2.层序遍历
3.连通块
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1000 + 10;
int n, m;
char g[N][N];
queue<PII> q;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs(int sx, int sy)
{
q.push({sx, sy});
g[sx][sy] = '#';
int res = 0;
while(q.size())
{
auto t = q.front();
q.pop();
res ++;
for(int i = 0; i < 4; i++)
{
int a = t.x + dx[i], b = t.y + dy[i];
if(a < 0 || a >= n || b < 0 || b >= m) continue;
if(g[a][b] == '#') continue;
q.push({a, b});
g[a][b] = '#';
}
}
return res;
}
int main()
{
while(cin>>m>>n, n || m)
{
for(int i = 0; i < n; i++) cin>>g[i];
PII start, end;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
{
if(g[i][j] == '@')
cout<<bfs(i, j)<<endl;
}
}
return 0;
}
两种搜索算法各有优劣,根据应用场景进行合适的选择
剪枝优化
两种搜索算法的一般适用于 n < 100左右的情况,所以有时会超时
于是需要剪去不满足条件的枝干
1.排列组合问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 30;
int n, m;
int path[N];
bool st[N];
void dfs(int pos, int start)
{
//剪枝
if(pos + n - start < m) return ;
if(pos > m)
{
for(int i = 1; i <= m; i++)
{
cout<<path[i]<<" ";
}
cout<<endl;
return ;
}
for(int i = start; i <= n; i++)
{
if(!st[i])
{
path[pos] = i;
st[i] = true;
dfs(pos + 1, i);
st[i] = false;
}
}
}
int main()
{
cin>>n>>m;
dfs(1, 1); // 位置,和选择的元素的起始位置
return 0;
}
\