区别与联系

区别

DFS多用于连通性问题因为其运行思想与人脑的思维很相似,故解决连通性问题更自然,采用递归,编写简便(但我个人不这样觉得。。。)

DFS的常数时间开销会较少。所以对于一些能用DFS就能轻松解决的,为何要用BFS?

一般来说,能用DFS解决的问题,都能用BFS

BFS多用于解决最短路问题,其运行过程中需要储存每一层的信息,所以其运行时需要储存的信息量较大,如果人脑也可储存大量信息的话,理论上人脑也可运行BFS。

Backtracking相当于在DFS的基础上进行剪枝

联系

BFS(显式用队列)

DFS(隐式用栈)(即递归)

当然,对于DFS,用递归可能会造成栈溢出,所以也可以更改为显示栈。

模板

在解答树里进行考虑

DFS/Backtracking

 1 void dfs(int 当前状态)
 2 {
 3     if(当前状态为边界状态)
 4     {
 5         记录或输出
 6         return;
 7     }
 8     for(i=0;i<n;i++)        //横向遍历解答树所有子节点
 9     {
10          //扩展出一个子状态。
11          修改了全局变量
12          if(子状态满足约束条件)
13          {
14              dfs(子状态)
15          }
16          恢复全局变量//回溯部分
17      }
18}

BFS

void bfs()
{
       q.push(s);                        //将(起始)首节点加入队列            
       visited[s]=true;                  //标记首节点已经被访问
       while(!q.empty())
       {
            int x=q.front();
            q.pop();
            遍历 x 的各个Next状态  next
            {
                 if(next is legal)
                 q.push(next);            //入队,同时计数或维护等; 
           }
        }  
}            

DFS(非递归,显式用栈)

 1 //求(sx,sy)到(ex,ey)的其中一条路径
 2 //如果无法到达,返回false
 3 bool dfs()
 4 {
 5     stack<P>s;
 6     vis[sx][sy] = true;      //访问
 7     s.push(P(sx, sy));    //入栈
 8     while (!s.empty())    //栈不为空。继续搜索;为空了还没有得到路径,说明无解
 9     {
10         P p = s.top(); 
11         int x = p.first, y = p.second;
12         if (x == ex && y == ey)
13         {
14             while (!s.empty())
15             {
16                                 //打印结果,或进行其它操作
17                 P p = s.top(); s.pop();
18                 printf("%d %d\n", p.first, p.second);
19             }
20             return true;
21         }
22         int flag = false;    //记录是否进入“死胡同”
23         for(遍历相邻的状态)
24         {
25                         if(满足条件)
26             {
27                 vis[nx][ny] = true;
28                 s.push(P(nx, ny));
29                 flag = true;      
30                 break;             //DFS,选择其中一条路走
31             }
32         }
33         if (!flag)
34             s.pop();  //周四是墙或已走过,回溯,也就是不断出栈,知道新的栈顶元素有其他出路
35     }
36     return false;
37 }

 

经典例题

部分和问题(DFS+剪枝)

给定整数a1、a2、...an,判断是否可以从中选出若干个数字,使它们的和恰好为k。(1≤n≤20)

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int maxn = 20 + 10;
 7 int n, a[maxn], k;
 8 int vis[maxn];
 9 
10 //已经从前cur项得到和sum
11 bool dfs(int cur, int sum)
12 {
13     if (sum > k)  return false;  //剪枝
14     if (cur == n)  return sum == k;  //如果前n项都计算过了,则返回sum是否等于k
15     
16     //不加上a[i]的情况
17     if (dfs(cur + 1, sum))      return true;
18     //加上a[i]的情况
19     if (dfs(cur + 1, sum + a[cur]))    return true;
20 
21     //无论是否加上a[i]都不能凑成k,则返回false
22     return false;
23 }
24 
25 int main()
26 {
27     while (scanf("%d%d", &n, &k) == 2 && n)
28     {
29         for (int i = 0; i < n; i++)
30             scanf("%d", &a[i]);
31         if (dfs(0, 0))        printf("YES\n");
32         else     printf("NO\n");
33     }
34     return 0;
35 }
View Code

相关文章: