当前位置:   article > 正文

2024/1/30 dfs与bfs

2024/1/30 dfs与bfs

想要了解dfs与bfs,就得了解队列和栈。

一、栈与队列

1.栈

栈说白了就是先入后出。把栈类比为一个容器。只有一个口,所以如果我们想要取出最底层也就是最先放入的元素,只能最后取出它。

栈基础操作有如下几种:

  1. push 放入
  2. pop 拿出
  3. empty 是否为空
  4. size 栈的大小
  5. top 获取栈顶元素

下面将用两种方式实现:

  1. 用数组或者链表模拟栈
    1. #include <iostream>
    2. using namespace std;
    3. const int N = 100;
    4. int stk[N], top = 0;
    5. int main()
    6. {
    7. //使用数组模拟栈
    8. int x;
    9. cin >> x;
    10. //push放入栈中
    11. stk[++top] = x;
    12. //输出栈顶元素
    13. cout << stk[top] << endl;
    14. //pop拿出(不管top元素删不删,top--就行)
    15. top--;
    16. // 常用的就是,获取栈顶元素的同时,弹出栈顶元素
    17. int u = stk[top--];
    18. return 0;
    19. }

    以上是普通静态数组。但是考虑到现实生活由于入栈的元素可能会源源不断地增加,因此我们可以使用动态数组或者链表,这样就无须自行处理数组扩容问题。这里贴出hello算法里对这个的网址。https://www.hello-algo.com/chapter_stack_and_queue/stack/#1icon-default.png?t=N7T8https://www.hello-algo.com/chapter_stack_and_queue/stack/#1

  2. stl库写栈

首先就是 #include <stack>

定义:stack<数据类型> stk;

使用:

  • stk.push(x);
  • stk.pop();
  • stk.top();
  • stk.empty();//true表示空
  • stk.size();//返回正整数
  1. #include <iostream>
  2. #include <stack>
  3. using namespace std;
  4. int main()
  5. {
  6. stack<int>stk;
  7. int x;
  8. cin >> x;
  9. //放入栈中
  10. stk.push(x);
  11. //输出栈顶
  12. cout << stk.top() << endl;
  13. //弹出栈顶
  14. stk.pop();
  15. return 0;
  16. }

2.队列

队列则是:“先入先出”。「队列 queue」是一种遵循先入先出规则的线性数据结构。顾名思义,队列模拟了排队现象,即新来的人不断加入队列尾部,而位于队列头部的人逐个离开。

队列基础操作有如下几种:

  1. push 放入(从队尾)
  2. pop 拿出(从队首)
  3. empty 是否为空
  4. size 栈的大小
  5. front
  1. #include <iostream>
  2. using namespace std;
  3. const int N = 100;
  4. int q[N], front = 0, tail = 0;
  5. int main()
  6. {
  7. //使用数组模拟队列
  8. int x;
  9. cin >> x;
  10. //push 放入队列中
  11. q[++tail] = x;
  12. //pop 把元素移除(只能从队首)
  13. front++;
  14. //size 队列长度
  15. int size = tail - front + 1;
  16. //empty 看size是否为0
  17. //front
  18. return q[front];
  19. return 0;
  20. }
  1. /* 初始化队列 */
  2. queue<int> queue;
  3. /* 元素入队 */
  4. queue.push(1);
  5. queue.push(3);
  6. queue.push(2);
  7. queue.push(5);
  8. queue.push(4);
  9. /* 访问队首元素 */
  10. int front = queue.front();
  11. /* 元素出队 */
  12. queue.pop();
  13. /* 获取队列的长度 */
  14. int size = queue.size();
  15. /* 判断队列是否为空 */
  16. bool empty = queue.empty();

额外:优先队列,双向队列

http://t.csdnimg.cn/KpFjNicon-default.png?t=N7T8http://t.csdnimg.cn/KpFjN5.2   队列 - Hello 算法 (hello-algo.com)icon-default.png?t=N7T8https://www.hello-algo.com/chapter_stack_and_queue/queue/#5215.3   双向队列 - Hello 算法 (hello-algo.com)icon-default.png?t=N7T8https://www.hello-algo.com/chapter_stack_and_queue/deque/

  1. /* 初始化双向队列 */
  2. deque<int> deque;
  3. /* 元素入队 */
  4. deque.push_back(2); // 添加至队尾
  5. deque.push_back(5);
  6. deque.push_back(4);
  7. deque.push_front(3); // 添加至队首
  8. deque.push_front(1);
  9. /* 访问元素 */
  10. int front = deque.front(); // 队首元素
  11. int back = deque.back(); // 队尾元素
  12. /* 元素出队 */
  13. deque.pop_front(); // 队首元素出队
  14. deque.pop_back(); // 队尾元素出队
  15. /* 获取双向队列的长度 */
  16. int size = deque.size();
  17. /* 判断双向队列是否为空 */
  18. bool empty = deque.empty();

双向队列兼具栈与队列的逻辑,因此它可以实现这两者的所有应用场景,同时提供更高的自由度

我们知道,软件的“撤销”功能通常使用栈来实现:系统将每次更改操作 push 到栈中,然后通过 pop 实现撤销。然而,考虑到系统资源的限制,软件通常会限制撤销的步数(例如仅允许保存 50 步)。当栈的长度超过 50 时,软件需要在栈底(队首)执行删除操作。但栈无法实现该功能,此时就需要使用双向队列来替代栈。请注意,“撤销”的核心逻辑仍然遵循栈的先入后出原则,只是双向队列能够更加灵活地实现一些额外逻辑。

二、DFS深度优先搜索-----栈

 1.指数型搜索

模板:

void dfs(int step){
    判断边界
    尝试每一种可能 for(i=1;i<=n;i++){
        继续下一步 dfs(step+1)
        }
    返回
}

  1. #include <iostream>
  2. using namespace std;
  3. const int N = 100;
  4. int n;//输入的节点数
  5. int st[N] = {}; //决策数组 0 表示没有搜索过, 1表示选择 , 2表示没有选择
  6. // 对于每一个点,都需要有一次决策,无论是1还是2
  7. // 搜索是把所有的情况都考虑
  8. // u表示对u进行抉择,已经抉择了u个数
  9. void dfs(int u) {
  10. // 递归出口,当所有节点都决策完毕时输出结果
  11. if (u > n){
  12. for (int i = 0; i < n; i++) {
  13. // 只有当第i个节点被选择时才输出
  14. if (st[u] = 1){
  15. cout << i << " ";
  16. }
  17. }
  18. }
  19. // 选择第u个节点(标记为1)并继续搜索下一个节点
  20. st[u] = 1;
  21. dfs(u + 1);
  22. // 恢复第u个节点的状态(标记为0)并选择不选择第u个节点(标记为2)并继续搜索下一个节点
  23. st[u] = 0;
  24. st[u] = 2;
  25. dfs(u + 1);
  26. // 继续恢复第u个节点的状态(标记为0)以备下次决策使用
  27. st[u] = 0;
  28. }
  29. int main()
  30. {
  31. cin >> n;
  32. dfs(1);
  33. return 0;
  34. }


Python Tutor code visualizer: Visualize code in Python, JavaScript, C, C++, and Javaicon-default.png?t=N7T8https://pythontutor.com/render.html#code=%23include%20%3Ciostream%3E%0A%0Ausing%20namespace%20std%3B%0A%0Aconst%20int%20N%20%3D%20100%3B%0A%0Aint%20n%20,%20st%5BN%5D%20%3D%20%7B%7D%3B%20//%200%20%E8%A1%A8%E7%A4%BA%E6%B2%A1%E6%9C%89%E6%90%9C%E7%B4%A2%E8%BF%87%EF%BC%8C%201%E8%A1%A8%E7%A4%BA%E9%80%89%E6%8B%A9%20%EF%BC%8C%202%E8%A1%A8%E7%A4%BA%E6%B2%A1%E6%9C%89%E9%80%89%E6%8B%A9%0A%0A//%20%E5%AF%B9%E4%BA%8E%E6%AF%8F%E4%B8%80%E4%B8%AA%E7%82%B9%EF%BC%8C%E9%83%BD%E9%9C%80%E8%A6%81%E6%9C%89%E4%B8%80%E6%AC%A1%E5%86%B3%E7%AD%96%EF%BC%8C%E6%97%A0%E8%AE%BA%E6%98%AF1%E8%BF%98%E6%98%AF2%20%0A//%20%E6%90%9C%E7%B4%A2%E6%98%AF%E6%8A%8A%E6%89%80%E6%9C%89%E7%9A%84%E6%83%85%E5%86%B5%E9%83%BD%E8%80%83%E8%99%91%20%0A%0A//%20u%E8%A1%A8%E7%A4%BA%E5%AF%B9u%E8%BF%9B%E8%A1%8C%E6%8A%89%E6%8B%A9%EF%BC%8C%E5%B7%B2%E7%BB%8F%E6%8A%89%E6%8B%A9%E4%BA%86u%E4%B8%AA%E6%95%B0%20%0Avoid%20dfs%28%20int%20u%20%29%20%7B%0A%20%20%20%20%0A%20%20%20%20//%20%E9%80%92%E5%BD%92%E5%87%BA%E5%8F%A3%20%EF%BC%8C%20%E6%AF%8F%E4%B8%80%E4%B8%AA%E7%82%B9%E9%83%BD%E6%8A%89%E6%8B%A9%E8%BF%87%E4%BA%86%20%0A%20%20%20%20if%28u%20%3E%20n%20%29%7B%0A%20%20%20%20%20%20%20%20for%28int%20i%20%3D%201%3B%20i%20%3C%3D%20n%20%3B%20i%2B%2B%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20//%20%E6%88%91%E4%BB%AC%E5%8F%AA%E6%9C%89%E5%9C%A8%E9%80%89%E6%8B%A9%E7%AC%ACi%E4%B8%AA%E7%82%B9%E7%9A%84%E6%97%B6%E5%80%99%E8%BE%93%E5%87%BA%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if%28st%5Bu%5D%20%3D%3D%201%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cout%20%3C%3C%20i%20%3C%3C%20%22%20%22%20%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20//%20%E9%80%89%E6%8B%A9%0A%20%20%20%20//%20st%5Bu%5D%20%3D%200%3B%20%E8%BF%99%E5%8F%A5%E8%AF%9D%E9%80%9A%E5%B8%B8%E7%9C%81%E7%95%A5%20%0A%20%20%20%20st%5Bu%5D%20%3D%201%3B%0A%20%20%20%20dfs%28u%20%2B%201%29%3B%0A%20%20%20%20%0A%20%20%20%20//%20%E6%81%A2%E5%A4%8D%E7%8E%B0%E5%9C%BA%EF%BC%8C%E6%8E%A7%E5%88%B6%E5%8F%98%E9%87%8F%20%EF%BC%9A%20%E5%8D%95%E4%B8%80%E5%8F%98%E9%87%8F%E5%8E%9F%E5%88%99%20%0A%20%20%20%20st%5Bu%5D%20%3D%200%3B%20%0A%20%20%20%20//%20%E4%B8%8D%E9%80%89%E6%8B%A9%20%0A%20%20%20%20st%5Bu%5D%20%3D%202%3B%0A%20%20%20%20dfs%28u%20%2B%201%29%3B%0A%20%20%20%20%0A%20%20%20%20//%20%E7%BB%A7%E7%BB%AD%E6%81%A2%E5%A4%8D%E7%8E%B0%E5%9C%BA%0A%20%20%20%20st%5Bu%5D%20%3D%200%3B%20%0A%7D%0A%0Aint%20main%28%29%7B%0A%20%20n%3D3%3B%0A%20%20%20%20dfs%281%29%3B%0A%20%20%20%20%0A%20%20%20%20return%200%3B%0A%7D&cppShowMemAddrs=true&cumulative=false&curInstr=38&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=cpp_g%2B%2B9.3.0&rawInputLstJSON=%5B%5D&textReferences=false

例题1 P2036 Perket

P2036 [COCI2008-2009 #2] PERKET - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P2036

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <cstdio>
  4. #include <cmath>
  5. using namespace std;
  6. // 全局变量声明
  7. int n; // n代表数字的个数
  8. int mindif = 999999999; // mindif用于存储最小差值,初始化为一个非常大的数
  9. int s[10], b[10]; // s数组和b数组分别用于存储输入的数字和它们对应的值
  10. // perket函数是一个递归函数,用于计算所有可能的组合
  11. // u代表当前处理的数字的索引
  12. // sp代表当前选择的s数组中数字的乘积
  13. // bs代表当前选择的b数组中数字的和
  14. void perket(int u, int sp, int bs) {
  15. // 递归终止条件:当u大于n时,表示所有数字都已经处理完毕
  16. if (u > n) {
  17. // 如果bs为0,表示没有选择任何b数组中的数字,直接返回
  18. if (!bs) {
  19. return;
  20. }
  21. // 更新最小差值
  22. mindif = min(mindif, abs(sp - bs));
  23. return;
  24. }
  25. // 递归调用1:不选择当前数字
  26. perket(u + 1, sp, bs);
  27. // 递归调用2:选择当前数字
  28. perket(u + 1, sp * s[u], bs + b[u]);
  29. }
  30. int main() {
  31. // 输入数字的个数
  32. cin >> n;
  33. // 输入每个数字及其对应的值
  34. for (int i = 1; i <= n; i++) {
  35. cin >> s[i] >> b[i];
  36. }
  37. // 调用perket函数开始计算
  38. perket(1, 1, 0);
  39. // 输出最小差值
  40. cout << mindif << endl;
  41. return 0;
  42. }

Python Tutor code visualizer: Visualize code in Python, JavaScript, C, C++, and Javaicon-default.png?t=N7T8https://pythontutor.com/render.html#code=%23include%20%3Ciostream%3E%20%20%0A%23include%20%3Calgorithm%3E%20%20%0A%23include%20%3Ccstdio%3E%20%20%0A%23include%20%3Ccmath%3E%20%20%0Ausing%20namespace%20std%3B%20%20%0A%20%20%0A//%20%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F%E5%A3%B0%E6%98%8E%20%20%0Aint%20n%3B%20//%20n%E4%BB%A3%E8%A1%A8%E6%95%B0%E5%AD%97%E7%9A%84%E4%B8%AA%E6%95%B0%20%20%0Aint%20mindif%20%3D%20999999999%3B%20//%20mindif%E7%94%A8%E4%BA%8E%E5%AD%98%E5%82%A8%E6%9C%80%E5%B0%8F%E5%B7%AE%E5%80%BC%EF%BC%8C%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%BA%E4%B8%80%E4%B8%AA%E9%9D%9E%E5%B8%B8%E5%A4%A7%E7%9A%84%E6%95%B0%20%20%0Aint%20s%5B10%5D,%20b%5B10%5D%3B%20//%20s%E6%95%B0%E7%BB%84%E5%92%8Cb%E6%95%B0%E7%BB%84%E5%88%86%E5%88%AB%E7%94%A8%E4%BA%8E%E5%AD%98%E5%82%A8%E8%BE%93%E5%85%A5%E7%9A%84%E6%95%B0%E5%AD%97%E5%92%8C%E5%AE%83%E4%BB%AC%E5%AF%B9%E5%BA%94%E7%9A%84%E5%80%BC%20%20%0A%20%20%0A//%20perket%E5%87%BD%E6%95%B0%E6%98%AF%E4%B8%80%E4%B8%AA%E9%80%92%E5%BD%92%E5%87%BD%E6%95%B0%EF%BC%8C%E7%94%A8%E4%BA%8E%E8%AE%A1%E7%AE%97%E6%89%80%E6%9C%89%E5%8F%AF%E8%83%BD%E7%9A%84%E7%BB%84%E5%90%88%20%20%0A//%20u%E4%BB%A3%E8%A1%A8%E5%BD%93%E5%89%8D%E5%A4%84%E7%90%86%E7%9A%84%E6%95%B0%E5%AD%97%E7%9A%84%E7%B4%A2%E5%BC%95%20%20%0A//%20sp%E4%BB%A3%E8%A1%A8%E5%BD%93%E5%89%8D%E9%80%89%E6%8B%A9%E7%9A%84s%E6%95%B0%E7%BB%84%E4%B8%AD%E6%95%B0%E5%AD%97%E7%9A%84%E4%B9%98%E7%A7%AF%20%20%0A//%20bs%E4%BB%A3%E8%A1%A8%E5%BD%93%E5%89%8D%E9%80%89%E6%8B%A9%E7%9A%84b%E6%95%B0%E7%BB%84%E4%B8%AD%E6%95%B0%E5%AD%97%E7%9A%84%E5%92%8C%20%20%0Avoid%20perket%28int%20u,%20int%20sp,%20int%20bs%29%20%7B%20%20%0A%20%20%20%20//%20%E9%80%92%E5%BD%92%E7%BB%88%E6%AD%A2%E6%9D%A1%E4%BB%B6%EF%BC%9A%E5%BD%93u%E5%A4%A7%E4%BA%8En%E6%97%B6%EF%BC%8C%E8%A1%A8%E7%A4%BA%E6%89%80%E6%9C%89%E6%95%B0%E5%AD%97%E9%83%BD%E5%B7%B2%E7%BB%8F%E5%A4%84%E7%90%86%E5%AE%8C%E6%AF%95%20%20%0A%20%20%20%20if%20%28u%20%3E%20n%29%20%7B%20%20%0A%20%20%20%20%20%20%20%20//%20%E5%A6%82%E6%9E%9Cbs%E4%B8%BA0%EF%BC%8C%E8%A1%A8%E7%A4%BA%E6%B2%A1%E6%9C%89%E9%80%89%E6%8B%A9%E4%BB%BB%E4%BD%95b%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E6%95%B0%E5%AD%97%EF%BC%8C%E7%9B%B4%E6%8E%A5%E8%BF%94%E5%9B%9E%20%20%0A%20%20%20%20%20%20%20%20if%20%28!bs%29%20%7B%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20return%3B%20%20%0A%20%20%20%20%20%20%20%20%7D%20%20%0A%20%20%20%20%20%20%20%20//%20%E6%9B%B4%E6%96%B0%E6%9C%80%E5%B0%8F%E5%B7%AE%E5%80%BC%20%20%0A%20%20%20%20%20%20%20%20mindif%20%3D%20min%28mindif,%20abs%28sp%20-%20bs%29%29%3B%20%20%0A%20%20%20%20%20%20%20%20return%3B%20%20%0A%20%20%20%20%7D%20%20%0A%20%20%20%20%20%20%0A%20%20%20%20//%20%E9%80%92%E5%BD%92%E8%B0%83%E7%94%A81%EF%BC%9A%E4%B8%8D%E9%80%89%E6%8B%A9%E5%BD%93%E5%89%8D%E6%95%B0%E5%AD%97%20%20%0A%20%20%20%20perket%28u%20%2B%201,%20sp,%20bs%29%3B%20%20%0A%20%20%20%20%20%20%0A%20%20%20%20//%20%E9%80%92%E5%BD%92%E8%B0%83%E7%94%A82%EF%BC%9A%E9%80%89%E6%8B%A9%E5%BD%93%E5%89%8D%E6%95%B0%E5%AD%97%20%20%0A%20%20%20%20perket%28u%20%2B%201,%20sp%20*%20s%5Bu%5D,%20bs%20%2B%20b%5Bu%5D%29%3B%20%20%0A%7D%20%20%0A%20%20%0Aint%20main%28%29%20%7B%20%20%0A%20%20%20%20//%20%E8%BE%93%E5%85%A5%E6%95%B0%E5%AD%97%E7%9A%84%E4%B8%AA%E6%95%B0%20%20%0A%20%20%20%20n%3D4%3B%0A%20%20%20%20%20%20%0A%20%20%20%20//%20%E8%BE%93%E5%85%A5%E6%AF%8F%E4%B8%AA%E6%95%B0%E5%AD%97%E5%8F%8A%E5%85%B6%E5%AF%B9%E5%BA%94%E7%9A%84%E5%80%BC%20%20%0A%20%20%20%20s%5B0%5D%3D1,s%5B1%5D%3D2,s%5B2%5D%3D3,s%5B3%5D%3D4%3B%0A%20%20%20%20b%5B0%5D%3D7,b%5B1%5D%3D6,b%5B2%5D%3D8,b%5B3%5D%3D9%3B%0A%20%20%20%20%20%20%0A%20%20%20%20//%20%E8%B0%83%E7%94%A8perket%E5%87%BD%E6%95%B0%E5%BC%80%E5%A7%8B%E8%AE%A1%E7%AE%97%20%20%0A%20%20%20%20perket%281,%201,%200%29%3B%20%20%0A%20%20%20%20%20%20%0A%20%20%20%20//%20%E8%BE%93%E5%87%BA%E6%9C%80%E5%B0%8F%E5%B7%AE%E5%80%BC%20%20%0A%20%20%20%20cout%20%3C%3C%20mindif%20%3C%3C%20endl%3B%20%20%0A%20%20%20%20%20%20%0A%20%20%20%20return%200%3B%20%20%0A%7D&cppShowMemAddrs=true&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=cpp_g%2B%2B9.3.0&rawInputLstJSON=%5B%5D&textReferences=false

在给定的代码中,每次递归的两种选择(选择当前数字和不选择当前数字)可能会产生重复的结果。这意味着,递归函数perket可能会计算出重复的组合。

为了解决这个问题,可以在递归过程中添加一些逻辑来跳过已经计算过的组合,或者使用其他方法来避免重复计算。例如,可以维护一个集合或哈希集合来跟踪已经计算过的组合,并在递归函数中进行检查。

如果不采取措施避免重复计算,则可能会导致递归函数的时间复杂度增加,因为重复计算相同的组合会导致更多的递归调用。在处理大量数据时,这可能会导致性能问题。

2.组合型搜索

例题2 全排列

【题意】
先给一个正整数 ( 1 < = n < = 10 ),输出所有全排列。
什么是全排列,例如n=3,输出所有组合,并且按字典序输出:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
每个全排列一行,相邻两个数用空格隔开(最后一个数后面没有空格)
【输入格式】
一行一个整数n。
【输出格式】
输出1~n的所有全排列。
【样例输入】
3
【样例输出】
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

  1. #include <iostream> // 引入输入输出流库
  2. #include <algorithm> // 引入算法库
  3. #include <cstdio> // 引入标准输入输出库
  4. #include <cmath> // 引入数学库
  5. using namespace std; // 使用标准命名空间
  6. int n, ans[10], used[10]; // 定义全局变量,n表示要选取的数字个数,ans数组用于存放结果,used数组用于标记数字是否被使用过
  7. void dfs(int cnt){ // 定义深度优先搜索函数,cnt表示当前已经选取的数字个数
  8. if (cnt > n) { // 如果已经选取的数字个数大于n,说明已经选取了所有数字,输出结果并返回
  9. for (int i = 1; i <= n; i++) { // 遍历所有数字并输出
  10. cout << ans[i] << " ";
  11. }
  12. cout << endl;
  13. return;
  14. }
  15. for (int i = 1; i <= n; i++){ // 枚举所有数字
  16. if (used[i] == 0) { // 如果数字i还没有被使用过
  17. ans[cnt] = i; // 将数字i放入当前位置
  18. used[i] = 1; // 标记数字i为已使用
  19. dfs(cnt + 1); // 递归搜索下一个位置
  20. ans[cnt] = 0; // 撤销当前位置的选择,即清空当前位置
  21. used[i] = 0; // 撤销对数字i的选择,即将其标记为未使用
  22. }
  23. }
  24. }
  25. int main() { // 主函数
  26. cin >> n; // 输入要选取的数字个数n
  27. dfs(1); // 从第一个位置开始搜索
  28. return 0; // 程序正常结束
  29. }

这段代码中的回溯是通过以下步骤实现的:

1. 在dfs函数中,首先检查是否已经选取了所有数字(即`cnt`是否大于`n`)。如果是,则输出当前结果并返回。
2. 然后,函数使用一个循环来枚举所有数字。如果某个数字还没有被使用过(即`used[i]`等于0),则进行以下操作:

  •     将该数字放入当前位置(即`ans[cnt] = i`)。
  •     标记该数字为已使用(即`used[i] = 1`)。
  •     递归调用`dfs(cnt + 1)`来搜索下一个位置。

3. 在递归调用返回后,执行回溯操作:

  •     撤销当前位置的选择,即将`ans[cnt]`重置为0,表示该位置还没有被选择。
  •     撤销对数字`i`的选择,即将`used[i]`重置为0,表示数字`i`现在可以再次被选择。

4. 重复上述过程,直到所有可能的排列都被探索完为止。

通过这种方式,代码能够撤销之前的决策,尝试其他可能的路径,从而全面地探索所有可能的解空间。

还有几个题,以后补补(太赶了qaq

三、BFS广度优先搜索-----队列

bfs个人感觉就是,同时往每个方向都试试,最先得到的就是最好的。

它是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。宽度优先搜索是一种对图进行搜索的算法。假设我们一开始位于某个顶点(即起点),此 时并不知道图的整体结构,而我们的目的是从起点开始顺着边搜索,直到到达指定顶点(即终 点)。在此过程中每走到一个顶点,就会判断一次它是否为终点。广度优先搜索会优先从离起点 近的顶点开始搜索。

b站有个视频非常清晰,这里贴下链接

【广度优先搜索BFS遍历-动画演示过程,希望对大家有所帮助~】

https://www.bilibili.com/video/BV1bP4y1K7Tr?vd_source=7ccfb0770f378b7710213a50fbacfc17icon-default.png?t=N7T8https://www.bilibili.com/video/BV1bP4y1K7Tr?vd_source=7ccfb0770f378b7710213a50fbacfc17

这个大佬写的也很清晰:

http://t.csdnimg.cn/sw4pZicon-default.png?t=N7T8http://t.csdnimg.cn/sw4pZ

int BFS(Node start, Node target) {
    入队(初始状态);
    visited[初始状态] = true;
    while(!空队) {
        for() { // 状态转换
            Node node = 队首元素;
 
            对node的处理,生成新node状态;
 
            if (visited[node] == true)
                continue;
            if (node == target) {
                输出答案;
                return 0;
            }
            v[node] = true;
            入队(node);
        }
    出队();
    }
    
}

 例题3 P1135 奇怪的电梯

P1135 奇怪的电梯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1135

  1. #include <iostream> // 引入输入输出流
  2. #include <cstring> // 引入C风格的字符串处理函数库,用于memset
  3. #include <queue> // 引入队列容器
  4. using namespace std;
  5. const int N = 201; // 定义常量N为201,表示图的最大节点数
  6. int n, a, b; // n为图的节点数,a为起点,b为终点
  7. int k[N], vis[N], ans[N]; // k数组存储每个节点的关联值,vis数组标记节点是否被访问过,ans数组存储从起点到每个节点的最短路径长度
  8. // BFS函数,从节点a开始搜索
  9. void bfs(int a) {
  10. queue<int> Q; // 定义一个队列Q
  11. Q.push(a); // 将起点a加入队列
  12. ans[a] = 0; // 设置起点a到自身的距离为0
  13. // 当队列不为空时继续循环
  14. while (!Q.empty()) {
  15. int u = Q.front(); // 取出队首元素
  16. Q.pop(); // 弹出队首元素
  17. // 如果当前节点是目标节点b,则结束搜索
  18. if (u == b) {
  19. break;
  20. }
  21. // 如果当前节点已经被访问过,则跳过
  22. if (vis[u] == 1) {
  23. continue;
  24. }
  25. vis[u] = 1; // 标记当前节点为已访问
  26. // 计算通过当前节点的关联值k[u]可以到达的两个节点v1和v2
  27. int v1 = u + k[u];
  28. // 如果v1是一个有效的节点且未被访问过,则将其加入队列,并更新ans[v1]
  29. if (v1 >= 1 && v1 <= n && vis[v1] == 0) {
  30. Q.push(v1);
  31. ans[v1] = min(ans[v1], ans[u] + 1);
  32. }
  33. int v2 = u - k[u];
  34. // 如果v2是一个有效的节点且未被访问过,则将其加入队列,并更新ans[v2]
  35. if (v2 >= 1 && v2 <= n && vis[v2] == 0) {
  36. Q.push(v2);
  37. ans[v2] = min(ans[v2], ans[u] + 1);
  38. }
  39. }
  40. }
  41. int main() {
  42. cin >> n >> a >> b; // 输入节点数n,起点a和终点b
  43. // 初始化ans数组为一个很大的数(表示无穷大)
  44. memset(ans, 0x3f, sizeof(ans));
  45. // 输入每个节点的关联值k[i]
  46. for (int i = 1; i <= n; i++) {
  47. cin >> k[i];
  48. }
  49. bfs(a); // 从起点a开始BFS搜索
  50. // 如果从a到b没有路径,则输出-1,否则输出最短路径长度
  51. if (ans[b] == 1e9) {
  52. cout << -1 << endl;
  53. } else {
  54. cout << ans[b] << endl;
  55. }
  56. return 0;
  57. }

四、BFS与DFS区别

bfs 遍历节点是先进先出,dfs遍历节点是先进后出;
bfs是按层次访问的,dfs 是按照一个路径一直访问到底,当前节点没有未访问的邻居节点时,然后回溯到上一个节点,不断的尝试,直到访问到目标节点或所有节点都已访问。
bfs 适用于求源点与目标节点距离最近的情况,例如:求最短路径。dfs 更适合于求解一个任意符合方案中的一个或者遍历所有情况,例如:全排列、拓扑排序、求到达某一点的任意一条路径。
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/aliyonghang/article/details/128724989

五、练习题

1.DFS

P1036 [NOIP2002 普及组] 选数P1036 [NOIP2002 普及组] 选数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1036P1219 [USACO1.5] 八皇后 Checker Challenge

P1219 [USACO1.5] 八皇后 Checker Challenge - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1219P1644 跳马问题

P1644 跳马问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1644P1605 迷宫

P1605 迷宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1605

2.BFS 

P1605 迷宫

P1605 迷宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1605P1443 马的遍历P1443 马的遍历 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1443

以后可能陆陆续续会更新一点这几篇内容,先记录一下

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

闽ICP备14008679号