DFSBFS和Backtracking模板

Posted lfri

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DFSBFS和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+剪枝)

给定整数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
");
32         else     printf("NO
");
33     }
34     return 0;
35 }
View Code

若要求输出所有的方案

技术分享图片
 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("
");
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
");
40         else  printf("No
");
41     }
42 }
View Code

联通块问题(DFS)

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

技术分享图片
 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
", 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)

技术分享图片
 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
");
67         else  printf("%d
", dis);
68     }
69     return  0;
70 }
View Code

八皇后问题(Backtracking)

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

技术分享图片
 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 :-------------
",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("
");
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

 

参考链接:

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

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

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

 

以上是关于DFSBFS和Backtracking模板的主要内容,如果未能解决你的问题,请参考以下文章

Eclipse 中的通用代码片段或模板

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段1——vue主模板

VSCode自定义代码片段2——.vue文件的模板

VSCode自定义代码片段(vue主模板)

分支回溯DFSBFS贪心算法二分查找