2025年广度优先搜索是回溯吗(广度优先搜索 回溯)

广度优先搜索是回溯吗(广度优先搜索 回溯)零 摘要 在这个专刊里 我会把所有算法都讲一遍 这章讲了搜索与回溯算法的原理和题目 一 搜索与回溯算法 搜索和回溯算法都是常用的问题求解方法 经常用于解决组合优化问题 如全排列 子集 组合等 以下是搜索和回溯算法的基本思想和示例代码 搜索算法的基本思想是通过遍历搜索问题的所有可能解 找到满足条件的解 搜索算法可以使用深度优先搜索 DFS



零.【摘要】:在这个专刊里,我会把所有算法都讲一遍,这章讲了搜索与回溯算法的原理和题目。

一.【搜索与回溯算法】:

搜索和回溯算法都是常用的问题求解方法,经常用于解决组合优化问题,如全排列、子集、组合等。以下是搜索和回溯算法的基本思想和示例代码:

  1. 搜索算法的基本思想是通过遍历搜索问题的所有可能解,找到满足条件的解。搜索算法可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来实现。

  2. 回溯算法是一种特殊的深度优先搜索算法,在搜索过程中,当发现当前的部分解不能满足问题的条件时,就会回溯到上一个状态,然后进行其他的尝试。回溯算法通常使用递归来实现。

二.【搜索之广度优先搜索】:

广度优先搜索(BFS,Breadth-First Search)是一种用于图和树等数据结构中的搜索算法。它从开始节点开始,逐层遍历,先访问离起始节点最近的节点,然后逐渐扩展到离起始节点更远的节点。

三【广度优先搜索的做法】:

  1. 创建一个队列(可以使用STL中的 )和一个visited数组(用于记录已经访问过的节点)。
  2. 将起始节点放入队列,并将其标记为已访问。
  3. 循环执行以下步骤,直到队列为空:
    • 从队列中取出队首节点。
    • 处理当前节点(例如,输出、记录或进行其他操作)。
    • 遍历当前节点的邻居节点:
      • 如果邻居节点未被访问过,则将其标记为已访问,并将其放入队列中。
  4. 完成搜索。

广度优先搜索的特点是按照节点的层次进行遍历,从起始节点开始,依次访问离起始节点最近的节点,然后再访问离起始节点较远的节点。

四.【广度优先搜索事例代码】:

 

输出结果为:0 1 2 3 4 5。

在示例代码中,我们使用一个  数组来记录已经访问过的节点,避免重复访问同一个节点。同时使用一个队列  来保存待访问的节点。我们从起始节点开始,将其标记为已访问并入队,然后循环进行弹出队首节点并打印,同时将其未访问的邻居节点标记为已访问并入队,直到队列为空。这样就完成了广度优先搜索的图遍历。

五.【搜索之深度优先搜索】:

深度优先搜索(DFS,Depth-First Search)是一种用于图和树等数据结构中的搜索算法。它从起始节点开始,通过依次访问节点的子节点,直到达到最深的节点,然后再回溯到上一层继续进行搜索。

六.【深度优先搜索的做法】:

  1. 创建一个visited数组(用于记录已经访问过的节点)。
  2. 从起始节点开始,调用递归函数进行深度优先搜索。
  3. 在递归函数中,首先处理当前节点(例如,输出、记录或进行其他操作)。
  4. 遍历当前节点的邻居节点:
    • 如果邻居节点未被访问过,则将其标记为已访问,并继续递归调用深度优先搜索函数。
  5. 完成搜索。

深度优先搜索的特点是优先访问离起始节点较远的节点,直到达到最深的节点,然后再回溯到上一层继续搜索。这种搜索方式类似于前序遍历树的方式。

七.【深度优先搜索事例代码】:

下面是一个使用深度优先搜索进行图遍历的示例代码:

 

输出结果为:0 1 3 4 2 5。

在示例代码中,我们使用一个  数组来记录已经访问过的节点,避免重复访问同一个节点。我们从起始节点开始,调用递归函数进行深度优先搜索。在递归函数中,首先处理当前节点,然后遍历当前节点的邻居节点,如果邻居节点未被访问过,则将其标记为已访问并继续递归调用深度优先搜索函数。这样就完成了深度优先搜索的图遍历。

八.【搜索之回溯】:

回溯(Backtracking)是一种在搜索算法中常用的技术,用于在问题的解空间中进行搜索并找到所有满足特定条件的解。它通常用于解决组合、排列、子集等相关问题。

九.【回溯的做法】:

  1. 定义一个解空间,即问题的可能解集合。
  2. 定义一个候选集,表示当前选择的候选项(可能是下一步可以选择的元素)。
  3. 使用递归函数进行回溯搜索:
    • 终止条件:
      • 如果满足特定条件(例如,找到了一个有效解),则记录当前解。
    • 在候选集中选择一个元素作为下一步的选择,并将其添加到当前解中。
    • 递归调用回溯函数,进行下一步的搜索。
    • 撤销当前选择,恢复到上一层的状态,继续搜索下一个候选项。
  4. 返回所有找到的解。

十.【回溯事例代码】:
 

下面是一个使用回溯算法求解全排列问题的示例代码:

 

输出结果为:

 

在示例代码中,我们使用回溯算法求解全排列问题。首先定义了一个  函数,它使用递归方式进行回溯搜索。在每一层的搜索中,我们遍历候选集中的所有元素,并选择一个未使用过的元素,将其添加到当前排列中,并继续递归搜索下一个位置。当排列的长度达到原始数组的长度时,我们将当前排列加入到结果集中。每次搜索完成后,我们撤销当前选择,并将其标记为未使用,然后继续搜索下一个候选项,直到搜索完所有的元素。最后返回所有找到的排列。

三.【操作步骤】

1. 定义问题:明确问题的输入、输出和约束条件。确定问题的定义和目标。

2. 设计数据结构:根据问题的特性,选择适当的数据结构来表示问题的状态和解空间。例如,使用数组、链表、树等来存储问题的状态。

3. 实现搜索函数:编写一个搜索函数,该函数接收当前状态和其他必要的参数,并返回下一步要探索的状态。搜索函数应根据问题的定义和约束条件进行适当的状态转换。

4. 实现回溯函数:编写一个回溯函数,该函数用于在搜索过程中回溯到上一个状态。回溯函数应该撤销前一步的操作,以便重新探索其他可能的路径。

5. 实现终止条件:确定搜索过程的终止条件。这可以是找到了满足问题定义的解,或者超过了一定的时间或深度限制。

6. 执行搜索:从初始状态开始,调用搜索函数进行搜索。在搜索的过程中,根据问题的约束条件和终止条件,进行状态转换和回溯操作。

7. 处理结果:根据搜索的结果,处理和输出满足问题定义的解。如果没有找到解,可以输出相应的提示信息。

需要注意以下几点:

- 确保正确性:在实现过程中,确保搜索和回溯函数的正确性。可以使用调试工具和单元测试来验证算法的正确性。

- 优化性能:对于复杂的问题,可能需要考虑优化搜索算法的性能。可以使用剪枝、启发式搜索等技术来提高搜索效率。

- 处理大规模问题:对于大规模问题,可能需要考虑使用适当的数据结构和算法来降低内存和时间的消耗。

这些步骤是一个一般性的指导,具体的实现过程会因问题的不同而有所差异。在实际应用中,可以根据具体问题的需求进行适当的调整和修改。

四.【题目讲解】:

1:自然数的拆分


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 32931     通过数: 19305

【题目描述】

任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。

当n=7共14种拆分方法:

7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
total=14

【输入】

输入n。

【输出】

按字典序输出具体的方案。

【输入样例】

7

【输出样例】

7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4

【算法思路】:

我们可以使用回溯法来解决这个问题。

回溯法的基本思路是不断尝试不同的拆分方式,然后递归地处理剩下的数。

具体步骤如下:

  1. 定义一个函数backtracking,参数为当前要拆分的数n和当前拆分方案cur_solution。

  2. 如果当前拆分方案的和等于n,将其加入到结果集中。

  3. 否则,从1到n-1遍历所有可能的拆分数:

    • 将当前拆分数加入到拆分方案中。

    • 调用backtracking函数处理剩下的数(即n-当前拆分数)。

    • 从拆分方案中移除当前拆分数,以便尝试其他可能的拆分。

  4. 按字典序输出结果集中的拆分方案。
    【代码实现】:

 

2:八皇后


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 21432     通过数: 13194

【题目描述】

会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 × 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。

对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2...b8,其中bi为相应摆法中第i行皇后所处的列数。已经知道8皇后问题一共有92组解(即92个不同的皇后串)。

给出一个数b,要求输出第b个串。串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小。

【输入】

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1≤b≤92)。

【输出】

输出有n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串。

【输入样例】

2
1
92

【输出样例】

15863724
84136275

【算法思路】

  1. 使用回溯法解决八皇后问题。
  2. 定义一个大小为8的数组queens,数组中的每个元素表示第i行皇后所在的列数。
  3. 定义一个递归函数backtracking来尝试放置皇后,参数为当前处理的行号row。
  4. 如果row等于8,表示已经找到了一组合法的解,将其转化为整数形式,并判断是否为目标解。
  5. 否则,从列1到8遍历当前行可放置皇后的位置,如果位置合法,则将该位置加入queens数组,然后递归调用backtracking处理下一行。
  6. 递归调用结束后,需要回溯,将queens数组当前位置的元素重置为0,以便尝试下一个位置。
  7. 不断重复步骤4~6,直到找到所有的解或达到目标解的编号b为止。
  8. 输出对应的皇后串。
     

【代码实现】:

 

这段代码实现了求解八皇后问题的主要逻辑。在主函数中,首先读入测试数据的组数n,然后进行n次循环,每次循环读入一个正整数b,表示要求解的第b个皇后串。接着调用backtracking函数求解八皇后问题,并将结果输出。

注意,这段代码没有包含输入输出的部分,只实现了求解八皇后问题的核心算法。在实际使用时,需要根据题目要求进行输入输出的处理。

3:单词接龙


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 11168     通过数: 6661

【题目描述】

单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如beast和astonish,如果接成一条龙则变为beastonish,另外相邻的两部分不能存在包含关系,例如at和atide间不能相连。

【输入】

输入的第一行为一个单独的整数n(n<=20)表示单词数,以下n行每行有一个单词(只含有大写或小写字母,长度不超过20),输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。

【输出】

只需输出以此字母开头的最长的“龙”的长度。

【输入样例】

5
at
touch
cheat
choose
tact
a

【输出样例】

23

【算法思路】: 

  1. 我们可以将单词为图的遍历问题。
  2. 首先构建一个图,每个单词作为一个节点,如果两个单词可以接在一起,则在两个节点之间连接一条边。例如,单词"at"和"touch"可以接在一起,它们的连接方式为"atouch"。
  3. 接下来,使用深度优先搜索(DFS)或广度优先搜索(BFS)遍历这个图,从给定字母开头的节点开始搜索,找出以该节点为起点的最长路径。
  4. 在搜索过程中,需要记录已经访问过的节点,以避免重复访问,并限制每个单词最多在路径中出现两次。
  5. 搜索结束后,找到最长路径的长度,并输出结果。

【代码实现】:

 

4:【例5.2】组合的输出


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 45855     通过数: 22910

【题目描述】

排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r≤n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。

现要求你用递归的方法输出所有组合。

例如n=5,r=3,所有组合为:

1 2 3   1 2 4   1 2 5   1 3 4   1 3 5   1 4 5   2 3 4   2 3 5   2 4 5   3 4 5

【输入】

一行两个自然数n、r(1<n<21,1≤r≤n)。

【输出】

所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。

【输入样例】

5 3

【输出样例】

  1  2  3
  1  2  4
  1  2  5
  1  3  4
  1  3  5
  1  4  5
  2  3  4
  2  3  5
  2  4  5
  3  4  5

 【算法思路】:

  1. 可以使用递归的方法输出所有组合。
  2. 递归的终止条件是当已经选取的元素数量等于需要选取的数量时,输出当前组合。
  3. 递归的过程是,对于当前可选元素的范围,依次选择一个元素加入组合中,然后递归调用函数选择剩下的元素,直到选择的元素数量达到需要选择的数量。
  4. 在递归调用之前,将已经选择的元素放入当前组合中,并传递已选择的元素的数量作为参数。
  5. 在递归调用之后,从当前组合中移除最后一个加入的元素,以便进行下一轮的选择。

【代码实现】:

 

5:红与黑


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 37425     通过数: 15413

【题目描述】

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

【输入】

包括多组数据。每组数据的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下:

1)‘.’:黑色的瓷砖;

2)‘#’:红色的瓷砖;

3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每组数据中唯一出现一次。

当在一行中读入的是两个零时,表示输入结束。

【输出】

对每组数据,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。

【输入样例】

6 9 
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
0 0

【输出样例】

45

 【算法思路】:

  1. 首先找到初始位置的坐标。
  2. 使用深度优先搜索(DFS)算法来计算从初始位置出发能到达的瓷砖数。
  3. 在深度优先搜索过程中,对于每个相邻的黑色瓷砖,进行递归调用,同时将当前瓷砖标记为已访问。
  4. 根据题目要求,每个相邻的瓷砖只能是上下左右四个方向之一,因此使用一个方向数组 dx[] 和 dy[] 来分别表示下一步在 x 轴和 y 轴的偏移量。
  5. 在递归调用之前,将当前瓷砖标记为已访问,即将其改变为红色。
  6. 在递归调用之后,将当前瓷砖标记为未访问,即将其改变为黑色。
  7. 统计访问过的黑色瓷砖数量。
  8. 搜索结束后,输出访问过的黑色瓷砖数量。

【算法流程】:

  1. 读入矩阵的宽度w和高度h。
  2. 如果w和h都为0,表示输入结束,退出循环。
  3. 初始化一个二维字符数组grid用于存储瓷砖的颜色。
  4. 初始化一个二维布尔数组visited用于标记瓷砖是否访问过。
  5. 在读入每个瓷砖的颜色时,如果颜色为'@',表示初始位置,记录初始位置的坐标。
  6. 调用dfs函数计算从初始位置出发能到达的瓷砖数,并输出结果。
  7. 重复步骤2-6,直到输入结束。

其中dfs函数使用深度优先搜索算法来计算从初始位置出发能到达的瓷砖数。对于每个相邻的黑色瓷砖,进行递归调用,并将当前瓷砖标记为已访问。搜索结束后返回访问过的黑色瓷砖数量。

【代码实现】:

 

创作不易,点个👍,观个注吧。

编程小号
上一篇 2025-01-27 20:06
下一篇 2025-02-17 16:57

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ri-ji/27845.html