DFS、BFS和Backtracking模板

区别与联系

区别

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)

《DFS、BFS和Backtracking模板》
《DFS、BFS和Backtracking模板》

 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

若要求输出所有的方案

《DFS、BFS和Backtracking模板》
《DFS、BFS和Backtracking模板》

 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],flag;
 9 
10 bool dfs(int cur, int sum)
11 {
12     if (cur == n)
13     {
14         if (sum == k)
15         {
16             flag = 1;
17             for (int i = 0; i < n; i++)
18                 if (vis[i]) printf("%d ", a[i]);
19             printf("\n");
20         }
21         return sum == k;
22     }
23     vis[cur] = false;        //不加上a[cur]
24     dfs(cur + 1, sum);        //这里不要return,都是在cur == n时(即遍历完了)在返回true/false
25 
26     vis[cur] = true;        //加上a[cur]
27     dfs(cur + 1, sum + a[cur]);
28 }
29 
30 int main()
31 {
32     while (scanf("%d%d",&n,&k) == 2 && n)
33     {
34         for (int i = 0; i < n; i++)
35             scanf("%d", &a[i]);
36         flag = 0;
37         dfs(0, 0);
38         if (flag)
39             printf("Yes\n");
40         else  printf("No\n");
41     }
42 }

View Code

联通块问题(DFS)

有一个大小为NxM的园子,雨后积起了水。八联通的积水被认为是连接在一起的。请求出园子里总共有多少个水洼?(N,M≤100)

《DFS、BFS和Backtracking模板》
《DFS、BFS和Backtracking模板》

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int maxn = 100 + 10;
 7 const int maxm = 100 + 10;
 8 const int dx[] = { -1,0,1,1,1,0,-1,-1 };
 9 const int dy[] = { 1,1,1,0,-1,-1,-1,0 };
10 int N, M;
11 char field[maxn][maxm];
12 
13 //现在位置(x,y)
14 void dfs(int x, int y)
15 {
16     field[x][y] = '.';    //表示已访问
17 
18     for (int i = 0; i < 8; i++)
19     {
20         int nx = x + dx[i], ny = y + dy[i];
21         if (nx >= 0 && nx < N && ny >= 0 && ny < M && field[nx][ny] == 'W')  dfs(nx, ny);
22     }
23     return;
24 }
25 
26 void slove()
27 {
28     int res = 0;        //记录联通块个数
29     for(int i = 0;i < N;i++)
30         for (int j = 0; j < M; j++)
31         {
32             if (field[i][j] == 'W')
33             {
34                 dfs(i, j);   //从有W的地方开始dfs,
35                 res++;
36             }
37         }
38     printf("%d\n", res);
39 }
40 
41 int main()
42 {
43     while (scanf("%d%d",&N,&M) == 2 && N)
44     {
45         for (int i = 0; i < N; i++)
46             scanf("%s", field[i]);
47         slove();
48     }
49     return 0;
50 }

View Code

最短路问题 (BFS)

给定一个大小为N x M的迷宫。迷宫由通道和墙壁组成,每步可以向邻接的上下左右四格的通道移动。请求出从起点到终点所需的最小步数。(N,M ≤ 100)

《DFS、BFS和Backtracking模板》
《DFS、BFS和Backtracking模板》

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<queue>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 typedef pair<int, int>  P;
 8 const int INF = 0x3f3f3f3f;
 9 const int maxn = 100 + 10;
10 const int maxm = 100 + 10;
11 const int dx[] = { -1,0,1,0 };
12 const int dy[] = { 0,1,0,-1 };
13 int N, M;
14 int sx, sy;            //起点
15 int ex, ey;            //终点
16 char maze[maxn][maxm];
17 int d[maxn][maxm];        //起点到各个位置的最短距离的数组
18 
19 //求(sx,sy)到(ex,ey)的最短距离
20 //如果无法到达,返回INF
21 int bfs()
22 {
23     queue<P>que;
24     for (int i = 0; i < N; i++)        //把所有位置初始化为INF
25         for (int j = 0; j < M; j++)
26             d[i][j] = INF;
27     que.push(P(sx, sy));  //将起点加入队列,且将距离置为0
28     d[sx][sy] = 0;
29 
30     while (!que.empty())
31     {
32         P p = que.front(); que.pop();
33         if (p.first == ex && p.second == ey)  break;  //如果到达终点,退出循环,由于bfs的特点,这时得到的就是最短距离
34 
35         for (int i = 0; i < 4; i++)
36         {
37             int nx = p.first + dx[i], ny = p.second + dy[i];
38             if(nx >= 0 && nx < N && ny >= 0 && ny < M && maze[nx][ny] != '#' && d[nx][ny] == INF)
39             {
40                 que.push(P(nx, ny));
41                 d[nx][ny] = d[p.first][p.second] + 1;
42             }
43         }
44     }
45     return d[ex][ey];
46 }
47 int main()
48 {
49     while (scanf("%d%d",&N,&M) == 2 && N)
50     {
51         for (int i = 0; i < N; i++)
52             scanf("%s", maze[i]);
53         for(int i = 0;i < N;i++)
54             for (int j = 0; j < M; j++)
55             {
56                 if (maze[i][j] == 'S')
57                 {
58                     sx = i; sy = j;
59                 }
60                 if (maze[i][j] == 'G')
61                 {
62                     ex = i; ey = j;
63                 }
64             }
65         int dis = bfs();
66         if (dis == INF)  printf("no way\n");
67         else  printf("%d\n", dis);
68     }
69     return  0;
70 }

View Code

八皇后问题(Backtracking)

在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

《DFS、BFS和Backtracking模板》
《DFS、BFS和Backtracking模板》

 1 1 #include<stdio.h>
 2  2 #include<iostream>
 3  3 #include<cmath>
 4  4 using namespace std;
 5  5 const int N = 8;
 6  6 int map[N][N];            
 7  7 int cnt = 0;            //记录方案数
 8  8 
 9  9 /************************打印结果********************/
10 10 void Display()
11 11 {
12 12     printf("--------------解决方案 %d :-------------\n",cnt);
13 13     for (int i = 0; i < N; i++)
14 14     {
15 15         for (int j = 0; j < N; j++)
16 16         {
17 17             if (map[i][j] == 0)
18 18                 cout << '.';
19 19             else
20 20                 cout << '#';
21 21         }
22 22         printf("\n");
23 23     }    
24 24 }
25 25 
26 26 /*********************判断是否与前面冲突****************/
27 27 int Check(int row, int col)
28 28 {
29 29     int flag = 1;
30 30     if (row == 0)
31 31         return true;
32 32     for (int i = 0; i < row; i++)
33 33     {
34 34         for (int j = 0; j < N; j++)
35 35         {
36 36             if (map[i][j] == 1)
37 37                 if (j == col || (fabs(row-i) == fabs(col - j)))
38 38                     flag = 0;
39 39         }
40 40     }
41 41     return flag;
42 42 }
43 43 
44 44 /**************************按行深搜***********************/
45 45 void Dfs(int row)
46 46 {
47 47     if (row == N)
48 48     {
49 49         cnt++;
50 50         Display();
51 51         return;
52 52     }
53 53     for (int col = 0; col < N; col++)
54 54     {
55 55         if (Check(row, col))
56 56         {
57 57             map[row][col] = 1;            //标记
58 58             Dfs(row + 1);
59 59             map[row][col] = 0;            //回溯,修改了的全局变量必须及时还原
60 60         }
61 61     }
62 62     return;
63 63 }
64 64 int main()
65 65 {
66 66     Dfs(0);
67 67     return 0;
68 68 }

View Code

 迷宫历经问题(DFS非递归)给定一个大小为N x M的迷宫。迷宫由通道和墙壁组成,每一步可以向邻接的上下左右四格的通道移动。请求出起点到终点的任意一条路径,若无法到达,输出”no way”

《DFS、BFS和Backtracking模板》
《DFS、BFS和Backtracking模板》

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<stack>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 typedef pair<int, int>  P;
 8 const int INF = 0x3f3f3f3f;
 9 const int maxn = 100 + 10;
10 const int maxm = 100 + 10;
11 const int dx[] = { -1,0,1,0 };
12 const int dy[] = { 0,1,0,-1 };
13 int N, M;
14 int sx, sy;            //起点
15 int ex, ey;            //终点
16 char maze[maxn][maxm];
17 bool vis[maxn][maxn];    
18 
19 //求(sx,sy)到(ex,ey)的其中一条路径
20 //如果无法到达,返回false
21 bool dfs()
22 {
23     stack<P>s;
24     vis[sx][sy] = true;
25     s.push(P(sx, sy));
26 
27     while (!s.empty())
28     {
29         P p = s.top(); 
30         int x = p.first, y = p.second;
31         if (x == ex && y == ey)
32         {
33             while (!s.empty())
34             {
35                 P p = s.top(); s.pop();
36                 printf("%d %d\n", p.first, p.second);
37             }
38             return true;
39         }
40         int flag = false;
41         for (int i = 0; i < 4; i++)
42         {
43             int nx = x + dx[i], ny = y + dy[i];
44             if (nx >= 0 && nx < N && ny >= 0 && ny < M && maze[nx][ny] != '#' && vis[nx][ny] == false)
45             {
46                 vis[nx][ny] = true;
47                 s.push(P(nx, ny));
48                 flag = true;
49                 break;     //DFS,选择其中一条路走
50             }
51         }
52         if (!flag)
53             s.pop();  //周四是墙或已走过,回溯,也就是不断出栈,知道新的栈顶元素有其他出路
54     }
55     return false;
56 }
57 int main()
58 {
59     while (scanf("%d%d", &N, &M) == 2 && N)
60     {
61         for (int i = 0; i < N; i++)
62             scanf("%s", maze[i]);
63         for (int i = 0; i < N; i++)
64             for (int j = 0; j < M; j++)
65             {
66                 if (maze[i][j] == 'S')
67                 {
68                     sx = i; sy = j;
69                 }
70                 if (maze[i][j] == 'G')
71                 {
72                     ex = i; ey = j;
73                 }
74             }
75         if (!dfs())  printf("no way\n");
76     }
77     return  0;
78 }

View Code

参考链接:

https://www.zhihu.com/question/28549888

https://blog.csdn.net/fightforyourdream/article/details/12866861

https://blog.csdn.net/renwotao2009/article/details/52993277

https://blog.csdn.net/lalor/article/details/6845788

https://blog.csdn.net/HUSTLX/article/details/52163923

 

    原文作者:Rogn
    原文地址: https://www.cnblogs.com/lfri/p/9769852.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞