• 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
    ", 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
    ");
    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

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

     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
    ", 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
    ");
    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

  • 相关阅读:
    Qt之使用CQU库快速开发统一风格界面
    Springboot中使用redis进行api防刷限流
    SpringBoot使用拦截器、过滤器、监听器
    ElasticSearch中文分词器-IK分词器的使用
    里式替换原则——面向对象程序设计原则
    使用Spring中的PropertyPlaceholderConfigurer读取文件
    新版本SpringCloud sleuth整合zipkin
    解放双手,在PC端进行Android真机调试
    破解Android设备无法联调的谜题
    打python&adb组合拳,实现微信读书永久免费读
  • 原文地址:https://www.cnblogs.com/lfri/p/9769852.html
Copyright © 2020-2023  润新知