搜索是什么?
搜索算法是利用计算机的高性能来有目的的穷举一个问题解空间的部分或所有的可能情况,从而求出问题的解的一种方法。现阶段一般有枚举算法、深度优先搜索、广度
优先搜索、A*算法、回溯算法、蒙特卡洛树搜索、散列函数等算法。在大规模实验环境中,通常通过在搜索前,根据条件降低搜索规模;根据问题的约束条件进行剪枝;
利用搜索过程中的中间解,避免重复计算这几种方法进行优化。摘自百度
目录
1.DFS
2.BFS
3.ID-DFS
4.小结
DFS
1.定义
DFS(Depth First Search),深度优先搜索算法,盲目搜索法。
算了,不写定义了,自己查。baidu
2.基本思想
为了求得问题的解,先选择某一种可能的情况向前探索,如果不对,立即退回再试,如此反复下去,直至得到问题的解或证明问题无解。
这就好比走迷宫:先选择一条路,如果走不通,就换另一条,直至到达终点或你不想玩了
3.实现方式(一般为栈或递归)
如图,访问顺序按字典序排列。
4.例题
题目描述
任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。
当n=7时,共有14种拆分方法:
7=1+1+1+1+1+1+1
=1+1+1+1+1+2
=1+1+1+1+3
=1+1+1+2+2
=1+1+1+4
=1+1+2+3
=1+1+5
=1+2+2+2
=1+2+4
=1+3+3
=1+6
=2+2+3
=2+5
=3+4
输入格式
输入n。
输出格式
按字典序输出具体的方案。
分析
(这道题相信大家都会,放代码吧)
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <climits>
#include <stack>
#include <queue>
#define MAXN 10005
using namespace std;
int n;
int a[MAXN] = {1};
void print(int n1) {
printf("%d", a[1]);
for(int i = 2; i <= n1 - 1; i ++) {
printf("+%d", a[i]);
}
printf("\n");
}
void dfs(int b, int tmp) {
if(b == 0) { //边界
print(tmp);
return;
}
for(int i = a[tmp - 1]; i <= b; i ++) { //当前的数要大于等于前一个数,且不超过n
if(i == n) continue;
a[tmp] = i; //保存拆分的数
dfs(b - i, tmp + 1);//继续递归
a[tmp] = 0;
}
}
int main() {
scanf("%d", &n);
dfs(n, 1); //调用函数
return 0;
}
5.DFS的优缺点
- 优点:空间复杂度比BFS小。
- 缺点:时间复杂度比BFS大很多,盲目搜索。
6.DFS的适用范围
- n<20左右(多数)
- 你想拿部分分
- 寻找路径&&满足第一条
分类标签上写着’DFS’直觉(很重要)
以上五点满足一点即可
BFS
1.定义
BFS(Breadth First Search),广度优先搜索算法,盲目搜寻法…
自己动手,丰衣足食→baidu
2.基本思想
从初始节点开始,生成第一层节点,如果目标不在其中,再从第一层的节点逐一扩展,直至找到目标节点。
这种搜索方式体现依层次横向扩展的趋势,所以叫广度优先搜索。
3.实现方式(一般为队列)
4.例题
题目描述
少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶。叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的
深处。迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全。现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方
格,而且那里会有神秘人物等待着他。现在要求你来帮助他实现这个目标。下图显示了一个迷阵的样例及李逍遥找到仙药的路线。
输入格式
输入有多组测试数据。每组测试数据以两个非零整数 M 和 N 开始,两者均不大于20。M 表示迷阵行数,N 表示迷阵列数。
接下来有 M 行,每行包含N个字符,不同字符分别代表不同含义:
‘@’:少年李逍遥所在的位置;
‘.’:可以安全通行的方格;
‘#’:有怪物的方格;
‘*’:仙药所在位置。
当在一行中读入的是两个零时,表示输入结束。
输出格式
对于每组测试数据,分别输出一行,该行包含李逍遥找到仙药需要穿过的最少的方格数目(计数包括初始位置的方块)。如果他不可能找到仙药,则输出-1。
分析
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <climits>
#include <queue>
#include <stack>
using namespace std;
const int MAXN = 25;
char a[MAXN][MAXN];
bool flag[MAXN][MAXN];
int xs[4] = {1, -1, 0, 0}, ys[4] = {0, 0, 1, -1};//用于转弯的数组
struct Node {
int x, y, step;
};
queue <Node> que;
Node qd, zd, t, d;
int main() {
int m, n, ans = 0;
while(1) {
scanf("%d %d", &m, &n);
if(m == 0 && n == 0) return 0;
while(que.size()) {//清空队列
que.pop();
}
ans = -1;
for(int i = 1; i <= m; i ++) {
scanf("\n");
for(int j = 1; j <= n; j ++) {
flag[i][j] = 0;//清空flag数组
scanf("%c", &a[i][j]);
if(a[i][j] == '@') {
qd.step = 0;
qd.x = i;
qd.y = j;
}
if(a[i][j] == '*') {
zd.x = i;
zd.y = j;
}
}
}
que.push(qd);
while(que.size()) {
t = que.front();
que.pop();
for(int i = 0; i < 4; i ++) {//各个方向
if(t.x + xs[i] >= 1 && t.x + xs[i] <= m && t.y + ys[i] >= 1 && t.y + ys[i] <= n && (!flag[t.x + xs[i]][t.y + ys[i]]) && (a[t.x + xs[i]][t.y + ys[i]] != '#')) { //扩展节点的条件
flag[t.x + xs[i]][t.y + ys[i]] = true;
d.x = t.x + xs[i];
d.y = t.y + ys[i];
d.step = t.step + 1;
if(d.x == zd.x && d.y == zd.y) {//满足条件,退出
ans = d.step;
break;
}
que.push(d);
}
}
if(ans > -1) break;
}
printf("%d\n", ans);
}
return 0;
}
5.BFS的优缺点
- 优点:与DFS相比降低了时间复杂度
- 缺点:牺牲了空间(换时间),盲目搜索
6.BFS的适用范围
1.部分求最短路径题目
2.n不能太大
3.分类标签上写着’BFS’
4.直觉(很重要)
ID-DFS
1.定义
ID-DFS,迭代加深(Iterative Deepening Search),或者更确切的说:迭代深化深度优先搜索(Iterative Deepening Depth-First Search)。
2.基本思想
DFS每次选定一个分支,不断深入,直至到边界才结束。设想一下,搜索树的分支非常多,且目标在一个较浅的节点上,如果选错了分支,就很可能浪费大量时间。
此时,迭代加深很好地优化了DFS:从小到大限制搜索的深度,如果在此深度搜不到答案,就把深度依次限制增加,重新搜索,直至找到答案。(用DFS模拟BFS)
3.实现方式(一般为递归)
4.例题
题目描述
已知一个数列 a0,a1,a2…am(其中 a0=1,am=n,a0<a1<a2<…<am),对于每个k,需要满足ak=ai+aj(0<=i,j<=k-1,这里i与j可以相等)。
现给定n的值,要求m的最小值(并不要求输出),及这个数列每一项的值(可能存在多个数列,只输出任一个满足条件的就可以了)。
输入格式
多组数据,每行给定一个正整数n。输入以0结束。
输出格式
对于每组数据,输出满足条件的长度最小的数列。
分析
此题分支较多,深度不大,所以可用迭代加深。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <climits>
#include <queue>
#include <stack>
using namespace std;
const int MAXN = 1005;
bool flag1;
int a[MAXN] = { 0, 1 };
void write(int x) {//正整数的写优
if (x == 0)
return;
write(x / 10);
putchar(x % 10 + '0');
}
void print(int j) {//输出
for (int i = 1; i <= j; i++) {
write(a[i]);
putchar(' ');
}
putchar('\n');
}
void dfs(int step, int tmp, int n) {//dfs
if (step == (tmp + 1)) {//到达限定深度:判断,退出。
if (a[tmp] == n && (!flag1)) {
print(tmp);
flag1 = true;
}
return;
}
for (int i = step - 1; i >= 1; i--) {
for (int j = i; j >= 1; j--) {
if (a[i] + a[j] > a[step - 1]) {
a[step] = a[i] + a[j];
dfs(step + 1, tmp, n);
a[step] = 0;
}
}
}
}
int main() {
int n;
while (1) {
scanf("%d", &n);
if (n == 0)
return 0;
flag1 = false;
for (int i = 1;; i++) {//迭代加深
dfs(2, i, n);
if (flag1) {
break;
}
}
}
return 0;
}
5.优点
空间复杂度和DFS一样低,时间复杂度与BFS相差不大,见证明
小结
搜索只是基础,剪枝特别难。学好了DFS、BFS、ID-DFS之后,搜索才算是入门了。多做题,多总结要点,搜索和其中的剪枝才迎刃而解。