当前位置:   article > 正文

dfs经典例题(入门题)_c++ dfs入门基础题

c++ dfs入门基础题

再附上一篇DFS详解的,不明白DFS原理的同学可以看一看:https://blog.csdn.net/li_jeremy/article/details/83714298   

 

 

以下是全网收集整理的和自己写的部分,绝对保证dfs轻松入门。

    核心代码:

    关于dfs参数问题,什么在变化,就把什么设置成参数。

 

 

    void dfs()//参数用来表示状态  
    {  
        if(到达终点状态)  
        {  
            ...//根据题意添加  
            return;  
        }  
        if(越界或者是不合法状态)  
            return;  
        if(特殊状态)//剪枝
            return ;
        for(扩展方式)  
        {  
            if(扩展方式所达到状态合法)  
            {  
                修改操作;//根据题意来添加  
                标记;  
                dfs();  
                (还原标记);  
                //是否还原标记根据题意  
                //如果加上(还原标记)就是 回溯法  
            }  
     
        }  
    }  

   为什么有的时候得要标记,因为这可以防止打转现象的发生,你可以想象如果没有标记,你在迷宫围绕着某条路径打转

  但有的时候需要标记却不需要还原标记,为何不需要还原标记,不还原的意义是防止下次递归时走到已走过的位置,还原是可   以让下次递归可以在经过原先递归走过的路径,当然不可能和原先一模一样的路径

 

     DFS全排列

  1. #include<iostream>
  2. using namespace std;
  3. int n;
  4. int book[10] = {0};
  5. int a[10];
  6. void dfs(int index)
  7. {
  8. if(index == n) //递归终止条件
  9. {
  10. for(int i = 0;i < n;++i)
  11. cout << a[i];
  12. cout << endl;
  13. return ;
  14. }
  15. else
  16. {
  17. for(int i = 1;i <= n;++i)
  18. {
  19. if(!book[i])
  20. {
  21. book[i] = 1;//标记位
  22. a[index] = i;//把符合的数字保存到数组中
  23. dfs(index+1);
  24. a[index] = 0;
  25. book[i] = 0;//还原标记,以便回溯
  26. }
  27. }
  28. }
  29. }
  30. void init()
  31. {
  32. for(int i = 0;i < 9;++i)
  33. book[i] = 0;
  34. }
  35. int main()
  36. {
  37. while(cin >> n)
  38. {
  39. dfs(0);
  40. init();
  41. }
  42. return 0;
  43. }

 

方格填数

如下的10个格子
   +--+--+--+
   |  |  |  |
+--+--+--+--+
|  |  |  |  |
+--+--+--+--+
|  |  |  |
+--+--+--+

(如果显示有问题,也可以参看【图1.jpg】)

填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)

一共有多少种可能的填数方案?

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

这就是道全排列的变式题(因为数字是0-9,且填入到格子中),因为它必须去检查所填的数字是否符合题目要求

思路:明显这是回溯题,可以用dfs做,当然你也可以套10个循环来做(TLE)

dfs的话这道题我们该如何进行递归,很明显这道题和全排列类似,所以我们可以一个方格一个方格来填空

由于题意要求连续的两个数字不能相邻

所以需要判断/左,上,左上,右上/,因为在你下面和右面的都还没填呢

  1. #include<iostream>
  2. #include<cmath>
  3. #include<sstream>
  4. #include<string>
  5. #include<cstring>
  6. #include<map>
  7. #include<set>
  8. #include<queue>
  9. #include<vector>
  10. #include<algorithm>
  11. using namespace std;
  12. int ans = 0;
  13. int dir[4][2] = {0,-1,-1,0,-1,-1,-1,1};
  14. int book[10][10];
  15. int a[10][10];
  16. int vis[10];
  17. /*左,上,左上,右上*/
  18. bool check(int r,int c,int val)
  19. {
  20. for(int k = 0;k < 4;++k)
  21. {
  22. int tr = r + dir[k][0];
  23. int tc = c + dir[k][1];
  24. if(tr < 1 || tc < 1 || tr > 3 || tc > 4) continue;
  25. if(abs(a[tr][tc]-val) <= 1) return false;
  26. }
  27. return true;
  28. }
  29. void dfs(int row,int col)
  30. {
  31. if(row == 3 && col == 4)
  32. {
  33. ans++;
  34. return ;
  35. }
  36. if(col == 5)
  37. {
  38. row += 1;
  39. col = 1;
  40. }
  41. for(int i = 0;i <= 9;++i)
  42. {
  43. if(!book[row][col] && !vis[i] && check(row,col,i))
  44. {
  45. book[row][col] = 1;
  46. vis[i] = 1;
  47. a[row][col] = i;
  48. dfs(row,col+1);
  49. book[row][col] = 0;
  50. vis[i] = 0;
  51. a[row][col] = -2;
  52. }
  53. }
  54. }
  55. int main()
  56. {
  57. for(int i = 0;i <= 5;++i)
  58. {
  59. for(int j = 0;j <= 9;++j)
  60. a[i][j] = -2;
  61. }
  62. dfs(1,2);
  63. printf("%d\n",ans);
  64. return 0;
  65. }

寒假作业

现在小学的数学题目也不是那么好玩的。
看看这个寒假作业:

   □ + □ = □
   □ - □ = □
   □ × □ = □
   □ ÷ □ = □
   
   (如果显示不出来,可以参见【图1.jpg】)
   
每个方块代表1~13中的某一个数字,但不能重复。
比如:
 6  + 7 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5

以及:
 7  + 6 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5

就算两种解法。(加法,乘法交换律后算不同的方案)
 
你一共找到了多少种方案?

同样是全排列变式题,

思路:一个一个填,递归的参数应该是下标,用一维数组来存储,一种做法是,把加减乘除比较,不符合则返回上一层,还有一种做法,把12个位置先填满,在判断是否符合条件,这样做,时间复杂度更高

  1. #include<iostream>
  2. #include<cmath>
  3. #include<sstream>
  4. #include<string>
  5. #include<cstring>
  6. #include<map>
  7. #include<set>
  8. #include<queue>
  9. #include<vector>
  10. #include<algorithm>
  11. using namespace std;
  12. int ans = 0;
  13. int vis[10];
  14. int a[13];
  15. void dfs(int index)
  16. {
  17. if(index == 13)
  18. {
  19. //为何不能a[10]/a[11] && a[10] % a[11] == 0, 11/5 == 2吧
  20. if(a[10] == a[11]*a[12])
  21. {
  22. ans++;
  23. }
  24. return ;
  25. }
  26. //+
  27. if(index == 3)
  28. {
  29. if(!vis[a[1]+a[2]])
  30. {
  31. vis[i] = 1;
  32. a[index] = i;
  33. dfs(index+1);
  34. vis[i] = 0;
  35. a[index] = 0;
  36. }
  37. return ;
  38. }
  39. //-
  40. if(index == 7)
  41. {
  42. if(a[4]-a[5] != a[6])
  43. return ;
  44. }
  45. //*
  46. if(index == 10)
  47. {
  48. if(a[7]*a[8] != a[9])
  49. return ;
  50. }
  51. for(int i = 1;i <= 13;++i)
  52. {
  53. if(!vis[i])
  54. {
  55. vis[i] = 1;
  56. a[index] = i;
  57. dfs(index+1);
  58. vis[i] = 0;
  59. a[index] = 0;
  60. }
  61. }
  62. }
  63. int main()
  64. {
  65. dfs(1);
  66. printf("%d\n",ans);
  67. return 0;
  68. }

   

剪邮票

如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。

请你计算,一共有多少种不同的剪取方法。

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

思路:也是一道全排列变式题,只不过这里我们只截取其中5个格子判断是否连通,把选中的五个棋子的位置保存起来,如何判断连通呢?回溯即可

  1. /*#include<iostream>
  2. #include<cmath>
  3. #include<sstream>
  4. #include<string>
  5. #include<cstring>
  6. #include<map>
  7. #include<set>
  8. #include<queue>
  9. #include<vector>
  10. #include<algorithm>
  11. using namespace std;
  12. int ans = 0;
  13. int vis[9][9];
  14. int dir[4][2] = {0,1,1,0,0,-1,-1,0};
  15. int book[10000][5][5];
  16. bool check(int count)
  17. {
  18. if(count == 0) return true;
  19. for(int k = 0;k < count;++k)
  20. {
  21. int flag = 1;
  22. for(int i = 1;i <= 3;++i)
  23. {
  24. for(int j = 1;j <= 4;++j)
  25. {
  26. if(book[k][i][j] != vis[i][j]) flag = 0;
  27. }
  28. }
  29. if(flag)
  30. return false;
  31. }
  32. return true;
  33. }
  34. void add(int a[][9],int count)
  35. {
  36. for(int i = 1;i <= 3;++i)
  37. {
  38. for(int j = 1;j <= 4;++j)
  39. {
  40. book[count][i][j] = vis[i][j];
  41. }
  42. }
  43. }
  44. void dfs(int r,int c,int sum)
  45. {
  46. if(sum == 4)
  47. {
  48. if(check(ans))
  49. {
  50. add(vis,ans);
  51. ans++;
  52. for(int i = 1;i <= 3;++i)
  53. {
  54. for(int j = 1;j <= 4;++j)
  55. {
  56. printf("%d ",vis[i][j]);
  57. }
  58. printf("\n");
  59. }
  60. printf("----------------------------------------------\n");
  61. }
  62. return ;
  63. }
  64. for(int k = 0;k < 4;++k)
  65. {
  66. int tr = r + dir[k][0];
  67. int tc = c + dir[k][1];
  68. if(tr < 1 || tc < 1 || tr > 3 || tc > 4) continue;
  69. if(!vis[tr][tc])
  70. {
  71. vis[tr][tc] = 1;
  72. dfs(tr,tc,sum+1);
  73. vis[tr][tc] = 0;
  74. }
  75. }
  76. }
  77. int main()
  78. {
  79. for(int i = 1;i <= 3;++i)
  80. {
  81. for(int j = 1;j <= 4;++j)
  82. {
  83. if(!vis[i][j])
  84. {
  85. vis[i][j] = 1;
  86. dfs(i,j,0);
  87. vis[i][j] = 0;
  88. }
  89. }
  90. }
  91. printf("%d\n",ans);
  92. return 0;
  93. }*/
  94. //别人的做法
  95. #include<iostream>
  96. #include<cmath>
  97. #include<sstream>
  98. #include<string>
  99. #include<cstring>
  100. #include<map>
  101. #include<set>
  102. #include<queue>
  103. #include<vector>
  104. #include<algorithm>
  105. using namespace std;
  106. int ans = 0;
  107. int que[13][2]; //保存信息
  108. int book[13][13];
  109. int a[10];
  110. int vis[14];
  111. int dir[4][2] = {0,1,1,0,0,-1,-1,0};
  112. int flag = 1;
  113. //通过回溯来判断
  114. int check(int x,int y)
  115. {
  116. if(book[x][y] == 0) return 0;
  117. book[x][y] = 0;
  118. return 1+check(x-1,y)+check(x+1,y)+check(x,y-1)+check(x,y+1);
  119. }
  120. void dfs(int sum,int last)
  121. {
  122. if(sum == 5)
  123. {
  124. memset(book,0,sizeof(book));
  125. //这里如何去判重
  126. for(int i = 0;i < 5;++i)
  127. {
  128. book[que[a[i]][0]][que[a[i]][1]]= 1;
  129. }
  130. flag = 1;
  131. if(check(que[last][0],que[last][1]) == 5)
  132. {
  133. ans++;
  134. }
  135. return ;
  136. }
  137. for(int i = last+1;i <= 12;++i)
  138. {
  139. if(!vis[i])
  140. {
  141. vis[i] = 1;
  142. a[sum] = i;
  143. dfs(sum+1,i);
  144. vis[i] = 0;
  145. a[sum] = -1;
  146. }
  147. }
  148. }
  149. int main()
  150. {
  151. int n = 1;
  152. for(int i = 1;i <= 3;++i)
  153. {
  154. for(int j = 1;j <= 4;++j)
  155. {
  156. //保存每个方格的横纵坐标
  157. que[n][0] = i;
  158. que[n++][1] = j;
  159. }
  160. }
  161. dfs(0,0);
  162. printf("%d\n",ans);
  163. return 0;
  164. }

    初学dfs:先做最最最基础的题: http://codeup.cn/contest.php?cid=100000608。

    做完了,你就入门了(绝对没有比这更基础的题了)

    然后再做下面的题:

 

    题型分类:

    写过这些入门题后,我们可以将DFS题分为两大类:


    1 . 地图型:这种题型将地图输入,要求完成一定的任务。因为地图的存在。使得题意清楚形象化,容易理清搜索思路。

        POJ 1031棋盘问题  (类似八皇后问题)http://poj.org/problem?id=1321

        AOJ 869-迷宫(遍历地图,四向搜索)   https://blog.csdn.net/chen_yuazzy/article/details/73656668
        HDU 1035-Robot Motion(指定方向搜索,迷路(循环)判断)   http://acm.hdu.edu.cn/showproblem.php?pid=1035
        HDU 1045-Fire Net(check函数,回溯)  http://acm.hdu.edu.cn/showproblem.php?pid=1045
        HDU 1010-Tempter of the Bone(奇偶剪枝,回溯)  http://acm.hdu.edu.cn/showproblem.php?pid=1010

    2 . 数据型:这种题型没有给定地图,一般是一串数字或字母,要求按照一定的任务解题。相对于地图型,这种题型较为抽象,需要在数据中进行搜索。数据以数组的形式存储,那么只要将数组也当作一张图来进行搜索就可以了。

        HDU 1016-Prime Ring Problem(回溯、素数筛)
        HDU 1258-Sum It Up(双重DFS递归,去重技巧)
        HDU 1015-Safecraker(回溯,字符处理)
        HDU 2676-Sudoku(抽象,回溯)

以下是一些DFS:

Sticks   http://acm.hdu.edu.cn/showproblem.php?pid=1455

http://acm.hdu.edu.cn/showproblem.php?pid=1455

http://acm.hdu.edu.cn/showproblem.php?pid=2553

http://acm.hdu.edu.cn/showproblem.php?pid=1426

http://acm.hdu.edu.cn/showproblem.php?pid=1528

http://acm.hdu.edu.cn/showproblem.php?pid=1175

http://acm.hdu.edu.cn/showproblem.php?pid=1010

http://acm.hdu.edu.cn/showproblem.php?pid=1312

http://acm.hdu.edu.cn/showproblem.php?pid=1716

http://acm.hdu.edu.cn/showproblem.php?pid=5547

http://acm.hdu.edu.cn/showproblem.php?pid=1181

http://acm.hdu.edu.cn/showproblem.php?pid=5305

记忆化搜索(DFS+DP):

http://acm.hdu.edu.cn/showproblem.php?pid=1978

http://acm.hdu.edu.cn/showproblem.php?pid=1078

http://acm.hdu.edu.cn/showproblem.php?pid=1428
 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/596610
推荐阅读
相关标签
  

闽ICP备14008679号