DFS和BFS的一点简单总结
搜索,简单狭窄地讲,就分为DFS与BFS,但是也有一些拓展的比如迭代深搜和IDA*搜索等等,搜索可以说是最基础却又是应用最广泛的算法。本文简单地总结下基础搜索在给定的显式图中的应用。
搜索与DP,贪心,分治的共同点在于状态的转移,只不过在搜索中,一个阶段的最优状态是由之前所有状态得到的,这是其余其他算法的区别之处。
状态的转移即由某一个不同的状态转移至另一个不同的状态,DFS在于从一个初始状态出发,一直转移状态直到搜到目标或搜到状态无法进行转移为止,其中的剪枝便是通过应题目具体要求和仔细分析而去掉某些没必要或不存在的状态以节约时间和空间,而BFS在于一层一层地扩展状态,这样首先找到的目标便一定是用时或步数最少的。DFS几乎适用于所有情况,因为这本身是一种遍历所有状态的搜索,只不过在寻找最小步或最小时间的情况下比较慢,无法快速找到目标,但在程序世界里DFS本身也有不适用的情况,如果状态没有下限即转移的深度没有下限那么便无法深搜,同样,BFS也可能存在一层状态都扩展不完的情况,这样便才会有迭代加深搜索等扩展搜索的出现,它结合了两者的优点且实际应用效果不错。紫书上面的名词“解答树”便很好地描述了所有状态转移的过程,当然这个似乎与我们无关,但是其实也是一种刻画搜索过程的有利工具。
下面便是我对搜索的心得与总结:
在具体的应用过程中:BFS和DFS通常是各有优点且相互结合,ACM届总存在各种各样不同的题,你无法直观判断你的代码是否正确,因为搜索题大多不能自己去想例子来测试,只能通过题目所给的样例来初步加以评判,不管是你是否得到正确答案,也可能出现各种卡时间、卡空间的题,因为搜索本身的状态数量非常大,在解答题目时你需要明白一下几点:
1、如何进行状态的转移,简单地说怎么进行下一步的选择,这个通常很简单。
2、是否需要标记已经转移过的状态以及怎么标记已经转移过的状态以不重复转移,你不能单一地判断,也就是要综合所给的题中所有出现的变化,在这个阶段中,我标记这个阶段时的状态,因为很可能不止“你”在变化,图中的情况也在变化。
3、怎么去除没必要的或不存在的状态,因为有些状态是不需要进行转移或扩展的。
弄清楚了以上三个问题,你就可以按题目要求从起始状态开始搜索了。
在解题的过程中,我也遇到了很多错误,当然可能更多错误我没遇到或即将遇到。
从两个简单的搜索题开始:
1、 poj3009 Curling 2.0
题目大意要求把一个冰壶从起点“2”用最少的步数移动到终点“3”,其中0为移动区域,1为石头区域,冰壶一旦想着某个方向运动就不会停止,也不会改变方向(想想冰壶在冰上滑动),除非冰壶撞到石头1或者到达终点3,冰壶撞到石头后,冰壶会停在石头前面,此时(静止状态)才允许改变冰壶的运动方向,而该块石头会破裂,石头所在的区域由1变为0. 也就是说,冰壶撞到石头后,并不会取代石头的位置。终点是一个摩擦力很大的区域,冰壶若到达终点3,就会停止在终点的位置不再移动。
首先读懂题意:沿着某一行出发会遇到石头才会停止,不停止则失败,停止时石头也会消失,还有就是我的步数不能超过10步,这就明显告诉我们当我转移的次数大于10时直接去掉,而且在我从某一次状态转移回溯后所有的情况应该没变,所以之前在上一次转移给石头的标记应该去掉,然后再重新进行下一次同一深度的转移,这个题中状态去重很简单,只是走过的点标记就行了。所以综合以上分析,我只要不断深搜取得最小步数就行了,但是之前的细节考虑是必须的。(这个问题讨论BFS不可行是没意义的,两者都可以,虽说是寻找最小步数但是这个题目从搜索的过程来看DFS更适合)
代码:
#include <cstdio>
int square[30][30];
int w, h, sx, sy;
int minnum;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
void DFS(int x, int y, int cnt)
{
if (cnt >= 10) return ;
int i, nx, ny;
for (i = 0; i < 4; i++){
int flag = 1, tag = 0;
int nx = x+dir[i][0];
int ny = y+dir[i][1];
while (1){
if (nx < 1 || nx > h || ny < 1 || ny > w){
flag = 0;
break;
}
if (square[nx][ny] == 1){
tag = 1;
break;
}
if (square[nx][ny]==3 && minnum > ++cnt){
minnum = cnt;
return ;
}
nx += dir[i][0];
ny += dir[i][1];
}
if (!flag) continue;
if (tag){
square[nx][ny] = 0;
nx -= dir[i][0];
ny -= dir[i][1];
if (nx == x && ny == y){
square[nx+dir[i][0]][ny+dir[i][1]] = 1;
continue;
}
DFS(nx, ny, cnt+1);
square[nx+dir[i][0]][ny+dir[i][1]] = 1;
}
}
}
int main()
{
int i, j;
while (scanf("%d %d", &w, &h), w+h){
minnum = 9999;
for (i = 1; i <= h; i++)
for (j = 1; j <= w; j++){
scanf("%d", &square[i][j]);
if (square[i][j] == 2){
sx = i;