c++ 算法之搜索
搜索 是 c++ 之中一个简单而重要的算法,每一个 OIer 必备的基础算法,你知道它究竟是什么吗?
搜索 是 c++ 之中一个简单而重要的算法,每一个 OIer 必备的基础算法,你知道它究竟是什么吗?
一、深度优先搜索
1.简介
深度优先搜索(简称“深搜”)是通过递归实现的,当搜索到一个元素时。如果这是最后一个元素,就退出递归,统计个数或输出路径。如果不是最后一个元素,就搜索他的下一个元素。由于在循环中直接就进入了下一层递归,所以它是先找一条路,找到头,在返回,再找一条路,找到头,再返回······(下图是一棵二叉树的深搜顺序):
这是核心代码框架:
void dfs(int x)
{
if(x == n)
{
// 找到一种结果,统计个数或输出路径。
return ;
}
for(int i = 0;i < n;i++)
{
if(!visit[i]/* 检查 i 是否用过 */ && /* 是否可以使用 i 的其他条件 */)
{
visit[i] = 1; // 将 i 标记为用过。
dfs(x + 1);
visit[i] = 0; // 回溯,搜索过 i 后 i 就可以使用了。
}
}
}
2.深搜的分支
1.搜索路径
搜索路径是深搜的重要分支,提到搜索路径,你应该能想到如下代码:
void dfs(int x,int y)
{
if(x == end_x && y == end_y)
{
// 找到一种结果,统计个数或输出路径。
return ;
}
if(!visit[x + 1][y]/* 检查 x+1,y 是否走过 */ && x + 1 <= n/*没出界*/ &&/* x+1,y 是否可以走,是不是障碍物 */)
{
visit[x + 1][y] = 1; // 将 x+1,y 标记为走过。
dfs(x + 1,y);
visit[x + 1][y] = 0; // 回溯,搜索过 x,y 后 x,y 就可以使用了。
}
if(!visit[x][y + 1] && y + 1 <= n &&/* x,y+1 是否可以走,是不是障碍物 */)
{
visit[x][y + 1] = 1;
dfs(x,y + 1);
visit[x][y + 1] = 0;
}
if(!visit[x - 1][y] && x - 1 >= 1 &&/* x-1,y 是否可以走,是不是障碍物 */)
{
visit[x - 1][y] = 1;
dfs(x - 1,y);
visit[x - 1][y] = 0;
}
if(!visit[x][y - 1] && y - 1 >= 1 &&/* x,y-1 是否可以走,是不是障碍物 */)
{
visit[x][y - 1] = 1;
dfs(x,y - 1);
visit[x][y - 1] = 0;
}
}
太长了
(不要和头文件比较,考试不会让你写头文件),可以开数组,加循环,但 x,y 两个变量怎么办?开两个数组。
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,1,-1};
这就有了新的 dfs 函数,可以把四个判断换成循环:
for(int i = 0;i < 4;i ++)
{
int tx = x + dx[i];
int ty = y + dy[i];
if(tx >= 1 && tx <= n && ty >= 1 && ty <= n)
{
dfs(tx,ty);
}
}
2.记忆化搜索
搜索时,难免有重复,怎么去重,那就把搜索到一个位置的答案存下来。
1.定义
定义 为搜索到 x,y 时的答案。
2.更新
只要在函数结束时更新答案即可。
3.剪枝
如果 (说明有了答案)即可直接返回答案。
3.例题
1.深さ優先探索
1.题目描述
高桥先生住的小区是长方形的,被划分成一个个格子。高桥先生想从家里去鱼店,高桥先生每次可以走到他前后左右四个格子中的其中一个,但不能斜着走,也不能走出小区。
现在给出地图:
s
:代表高桥先生的家
g
:代表鱼店
.
:代表道路
#
:代表墙壁高桥先生不能穿过墙壁。
2.代码
#include <iostream>
using namespace std;
bool mp[502][502];
int dx[4] = {-1,1,0,0};
int dy[4] = {0,0,-1,1};
int x2,y2;
bool f;
void dfs(int n,int m)
{
if(n == x2 && m == y2) f = true; // 搜索结束,找到答案。
if(f) return ; // 有了答案,不用继续搜索了。
if(!mp[n][m]) return ; // 不能走
mp[n][m] = 0; // 标记走过,防止重复
for(int i = 0;i < 4;i ++){
dfs(n + dx[i],m + dy[i]); // 搜索下一个
}
return ;
}
int main()
{
int n,m,x1,y1;
char temp;
cin >> n >> m;
for(int i = 1;i <= n;i ++){
for(int j = 1;j <= m;j ++){
cin >> temp;
if (temp == '.'){
mp[i][j] = true;
}
else if(temp == 's'){
x1 = i;
y1 = j;
mp[i][j] = true;
}
else if(temp == 'g'){
x2 = i;
y2 = j;
}
}
}
dfs(x1,y1);
if(f) cout << "Yes";
else cout << "No";
return 0;
}
请勿抄袭代码!!!
详见洛谷社区规则:
用户有如下违反学术诚信的行为,视情节严重性,可处以警告或棕名的处罚:
- 评测时多次提交抄袭他人的代码;
- 在公开比赛期间作出 洛谷公开比赛参赛规则 提到的违反学术诚信行为;
- 提交题解审核的博客中,含有抄袭他人的内容;
- 其他违反洛谷学术诚信的行为
2.滑雪
本题仅提供思路:
1.请记忆化搜索!
2.注意不走回头路!
二、广度优先搜索
1.简介
和深搜不一样,广度优先搜索(简称广搜)又称层次遍历,顾名思义,一层一层的搜索:
怎么实现呢?总不能搞一个数组存顺序吧?不用, c++ STL 中自带 queue (队列)我们可以用队列代替数组,存搜索顺序。每此从队列中拿出一个元素,如果是最后一个元素,就 break ,统计路径或输出,否则就把他的子节点入队。注意⚠️:要先把第一个节点入队,要不然什么也搜不到!!!
queue <int> q;
while(!q.empty())
{
int start = q.front(); // 拿出队首元素
q.pop(); // 出队一个元素
if(start == n)
{
// 找到一条路,处理。
}
for(int i = 0;i < mp[start].size();i ++)
{
q.push(mp[start][i]); // 入队它的字节点
}
}
2.例题
马的遍历 :
1.题目描述
有一个 n×m 的棋盘,在某个点 (x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。
2.代码
#include<iostream>
#include<queue>
#include<map>
using namespace std;
int b[401][401],n,m;
int dx[8] = {1,1,2,2,-1,-1,-2,-2};
int dy[8] = {2,-2,1,-1,2,-2,1,-1};
int main(){
int x,y;
queue <pair<int,int> > q;
cin >> n >> m >> x >> y;
for(int i = 1;i <= n;i ++){
for(int j = 1;j <= m;j ++){
b[i][j] = -1;
}
}
b[x][y] = 0;
q.push(make_pair(x,y));
while(!q.empty()) {
int x0 = q.front().first;
int y0 = q.front().second;
q.pop();
for(int i = 0;i < 8;i ++){
int x1 = x0 + dx[i],y1 = y0 + dy[i];
if(x1 > n || x1 < 1 || y1 > m || y1 < 1 || b[x1][y1] != -1) continue;
b[x1][y1] = b[x0][y0] + 1;
q.push(make_pair(x1,y1));
}
}
for(int i = 1;i <= n;i ++){
for(int j = 1;j <= m;j ++){
cout << ' ' << b[i][j];
}
cout << endl;
}
return 0;
}
你学会了吗?
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)