穷竭搜索

本文深入讲解穷极搜索的两大核心方法——深度优先搜索与广度优先搜索,通过具体实例介绍如何利用递归、栈和队列等数据结构解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

穷极搜索主要包括两个方面:
1.深度优先搜索
2.广度优先搜索

一些基本的思想
1.递归函数
在函数中调用自己的函数就是递归函数,例如阶乘函数可以定义为:
int fact(int n)
{
if (n == 0) return 1;
return n * fact(n - 1);
}
递归最重要的一点就是函数的停止条件,在上面的例子中,当传入的参数为0时,就停止递归。
假如一个函数递归没有终结条件,那么将会反复调用自身,直到栈溢出。

斐波那契数列的例子如下:
int fib(int n)
{
if ( n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
注意到,在这个递归调用时, x一些情况被重复计算了,所以如果能有一张表记录以前的计算结果,那么再次运行时就不需要重新计算,将会省去大量时间。
改写后的程序如下:
int memo[MAX_N+1];

int fib(int n)
{
if ( n <=1 ) return n;
if(memo[n] != 0) return memo[n];
return memo[n] = fib(n - 1) + fib(n - 2);
}

2.栈
先进后出的数据结构,可以使用C++标准库中的stack实现
一些操作的例子如下:
stack<int> s;
s.push(1);
s.top();
s.pop();

3.队列
先进先出的数据结构,可以使用C++标准库中的queue实现
一些操作的例子如下:
queue<int> que;
que.push(1);
que.front();
que.pop();

深度优先搜索(DFS)
深度优先主要通过状态的转移不断的寻找下一个状态,知道不能够继续转移了,则向前回溯一个状态,查看有没有其他状态可以转移,知道搜索完所有状态。

例子:部分和问题

题目要求:给定整数a1,a2...an,判断是否可以选择若干个数,使其和恰好为k
例如:
输入  n=4 a={1,2,4,7} k=13
输出  yes(13 = 2 + 4 + 7)
输入  n=4 a={1,2,4,7} k=15
输出  no

分析:这里最直接的方法就是通过试试所有的方案看看加起来有没有等于k的情况,但难点就在于怎么去枚举所有的情况,这里需要一些小技巧:我们发现,对于每一个ai,只有两种选择,加上或者不加上,那么就可以将所有的输入组织成一棵二叉树,每一个节点代表当前的数字现在所有的和是多少,左边节点代表不加上当前的数字,右边节点代表加上这个数字。这样,只要遍历整棵树,就可以了解所有情况。
代码如下:
int a[MAX_N];
int n, k;

/*计算第i个数的时候此时总和为sum,此时就有两种选择,加上i或者不加i,分别使用两次递归*/
bool dfs(int i,int sum)
{
if(i == n) return sum = k;
if(dfs(i + 1, sum)) return true;
if(dfs(i + 1, sum + a[i])) return true;
return false;
}
void solve()
{
if(dfs(0, 0)) printf("yes");
else printf("no");
}

广度优先搜索(BFS)
广度优先总是搜索距离初始状态近的状态,并由近至远依次搜索其他的状态,直到搜索完毕
在数据结构上,使用队列可以很方便的完成广度搜索的方法,首先将距离初始状态最近的状态如队列,然后从队列中不断的取出状态,然后把取出的状态可以转移到的其他状态如队列,如此往复,直到队列为空。

例子:迷宫的最短路径

题目要求:给定一个大小为N*M的迷宫,迷宫由通道和墙壁组成,每一步可以向邻接的上下左右4格作出移动,求从起点到终点移动的最少步数。
例如:
输入:  #S######.#
   ......#..#
       .#.##.##.#
       .#........
       ##.##.####
   ....#....#
   .#######.#
   ....#.....
   .####.###.
   ....#...G#
输出:22
分析:无论深度优先还是广度优先,都涉及到了状态的转移,在迷宫问题中,每个状态就是所在位置的坐标,所以使用pair来表示状态。转移的方向为4个方向。在搜索的过程中,只需要将访问的距离

代码如下:
const int INF = 1000000;

typedef pair<int, int> P;                              //迷宫的坐标使用pair表示

char maze[MAX_N][MAX_M+1];                //表示迷宫的字符串数组
int N, M;
int sx, sy;                                                        //起点坐标
int gx, gy;                                                       //终点坐标

int d[MAX_N][MAX_M];                               //到各个位置距离最短的数组

/*四个方向的表示,注意这里使用了两个数组,这样在后面可以使用一个循环就简单的访问四个方向*/
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};

int bfs()
{
queue<P> que;
/*所有位置初始化为INF*/
for (int i = 0; i < N; i++)
for (int j = 0; j < M; j++) d[i][j] = INF;
/*将起点入队列,并将距离设置为0*/
que.push(P(sx, sy));
d[sx][sy] = 0;

while (que.size())
{
/*从队列最前端取出元素*/
P p = que.front(); que.pop();
/*如果已经是终点,停止搜索*/
if (p.first == gx && p.second == gy) break;

/*四个方向的循环*/
for (int i = 0; i < 4; i++)
{
/*新的坐标位置*/
int nx = p.first + dx[i], ny = p.second + dy[i];

/*判断是否可以移动或者或者已经访问过了*/
if (0 <= nx && nx < N &&0 <=ny &&ny < M && maze[nx][ny] != '#' && d[nx][ny] == INF)
{
/*可以移动的话将其入队列,并将该位置确定为p位置+1*/
que.push(P(nx, ny));
d[nx][ny] = d[p.first][p.second] + 1;
}
}
}
return d[gx][gy];
}
void solve()
{
int res = bfs();
printf("%d\n", res);
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值