回溯算法中关于剪枝的一些应用

news/2025/2/8 14:40:00/

在这里插入图片描述
衔接上篇( ^ _ ^ )
剪枝优化是回溯算法中一种重要的优化手段,其核心思想是 提前终止无效的递归分支,避免无意义的搜索,从而大幅减少计算量。通过合理剪枝,可以将指数级的时间复杂度降低到更优的水平。


一、剪枝的核心思想

在递归遍历决策树时,提前判断当前路径是否可能得到有效解

  • 不可能得到解 → 直接终止当前分支的递归(“剪掉”这个分支)
  • 可能得到解 → 继续递归探索

二、剪枝的常见类型及示例

1. 可行性剪枝

在每一步判断当前路径是否满足问题的约束条件,若不满足则提前返回。
示例:组合总和问题

void backtrack(vector<int>& nums, int remain, int start, vector<int>& path, vector<vector<int>>& res) {if (remain < 0) return;  // ✂️剪枝:剩余值为负数时直接返回if (remain == 0) {res.push_back(path);return;}for (int i = start; i < nums.size(); ++i) {if (nums[i] > remain) break;  // ✂️剪枝:已排序,后续元素更大,无需尝试path.push_back(nums[i]);backtrack(nums, remain - nums[i], i, path, res);path.pop_back();}
}

关键点

  • 先对数组排序(sort),使后续元素递增
  • nums[i] > remain 时,后续元素必然更大,不可能满足条件 → 直接break

2. 重复性剪枝

避免生成重复的解(常见于元素有重复值的问题)。
示例:全排列II(含重复元素)

void backtrack(vector<int>& nums, vector<bool>& used, vector<int>& path, vector<vector<int>>& res) {if (path.size() == nums.size()) {res.push_back(path);return;}for (int i = 0; i < nums.size(); ++i) {// ✂️剪枝条件1:跳过已使用的元素if (used[i]) continue;  // ✂️剪枝条件2:跳过重复元素(需先排序)if (i > 0 && nums[i] == nums[i-1] && !used[i-1]) continue;used[i] = true;path.push_back(nums[i]);backtrack(nums, used, path, res);path.pop_back();used[i] = false;}
}

关键点

  • 先对数组排序(sort),使相同元素相邻
  • nums[i] == nums[i-1]!used[i-1] 时,说明前一个相同元素未被使用 → 当前分支会产生重复解 → 跳过

3. 对称性剪枝

利用问题本身的对称性,避免重复搜索对称解。
示例:生成回文数
在生成回文数时,只需构造前半部分,后半部分对称生成即可。


三、剪枝的实现技巧

技巧适用场景示例
排序 + 提前终止循环组合问题、子集问题if (nums[i] > remain) break
索引标记(start避免重复选择元素组合问题中的 for (i = start)
哈希表记录已访问状态棋盘类问题(如数独)记录某行/列/宫是否包含某数字
数学性质推导利用数学公式提前排除不可能的情况N皇后问题的斜线检查

四、剪枝的意义

  1. 时间复杂度优化:将指数级复杂度降低到更优水平(例如从 O(n!) 优化到 O(n×n!))
  2. 空间优化:减少递归深度和内存占用
  3. 实际效率提升:对于大规模输入,剪枝可能将不可行的问题变为可解

五、经典剪枝练习

  1. 组合总和II(不可重复选相同元素):if (i > start && nums[i] == nums[i-1]) continue
  2. 子集II(含重复元素):if (i > start && nums[i] == nums[i-1]) continue
  3. 数独求解:预先记录每行/列/宫已使用的数字,快速判断是否可填

六、总结

剪枝是回溯算法的灵魂,核心在于通过问题特性提前终止无效递归
关键步骤

  1. 明确问题的约束条件
  2. 分析决策树中哪些分支必然无效
  3. 设计剪枝条件,用代码实现提前终止
  4. 通过排序、哈希表等工具辅助剪枝判断

http://www.ppmy.cn/news/1570339.html

相关文章

机器学习常用包pandas篇(二)数据选择、删减、缺失值处理和可视化

目录 前言 1. 基于数字索引选择&#xff08;iloc&#xff09; 2. 基于标签名称选择&#xff08;loc&#xff09; 二、数据删减 1. 删除行/列&#xff08;drop&#xff09; 2. 数据去重&#xff08;drop_duplicates&#xff09; 3. 删除缺失值&#xff08;dropna&#xff…

source 与 shell 之详解(Detailed Explanation of Source and Shell)

source 命令与 shell 变量 随着IC工具的升级迭代&#xff0c;不同项目使用到的 IC 工具版本可能会不一样。为保证 IC 工具版本和芯片项目的对应&#xff0c;需要使用 source 命令执行对应项目的环境变量设置脚本。那么&#xff0c;source 命令与一般的脚本执行命令&#xff0c…

每日Attention学习21——Cascade Multi-Receptive Fields

模块出处 [MICCAI 24] [link] TinyU-Net: Lighter Yet Better U-Net with Cascaded Multi-receptive Fields 模块名称 Cascade Multi-Receptive Fields (CMRF) 模块作用 轻量感受野块 模块结构 模块特点 起点使用PWConv(PointWise Convolution, 11卷积)压缩通道&#xff0c…

【C语言】数组名及其地址的理解与应用

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 &#x1f4af;前言&#x1f4af;数组名的本质1. 数组名实际上是一个指向第一个元素的指针2. 数组名与数组首元素地址的关系 &#x1f4af;数组名与指针算术操作1. 数组名的指针特性2. 数组名与数组整体…

四、GPIO中断实现按键功能

4.1 GPIO简介 输入输出&#xff08;I/O&#xff09;是一个非常重要的概念。I/O泛指所有类型的输入输出端口&#xff0c;包括单向的端口如逻辑门电路的输入输出管脚和双向的GPIO端口。而GPIO&#xff08;General-Purpose Input/Output&#xff09;则是一个常见的术语&#xff0c…

GlusterFS源码讲解:如何实现最终一致性

引言 在分布式文件系统中&#xff0c;由于网络延迟、节点故障或临时分区原因&#xff0c;很难保证写操作在所有节点上立即生效。为了解决这一问题&#xff0c;很多系统采用最终一致性模型&#xff1a;写操作可能一开始没有同步到所有节点&#xff0c;但经过一段时间后&#xff…

mobaxterm 无法ssh连接ubuntu

0.查看IP地址 BASH homename -I ip addr show 1. 确保安装了 openssh-server 首先&#xff0c;确保你已经安装了 openssh-server&#xff0c;这是提供 SSH 服务的关键包。 步骤&#xff1a; 打开终端并更新包列表&#xff1a; BASH sudo apt update 安装 openssh-serve…

FocusAny v0.6.0 MacOS和Linux安装优化,独立窗口显示优化

FocusAny 是一个专注高效的AI工具条&#xff0c;可以使用 Alt / Option空格 一键唤起&#xff0c;通过插件快速安装&#xff0c;可以扩展出非常多的功能。 安装使用 访问 https://focusany.com 下载 对应系统 安装包&#xff0c;一键安装即可。 目前支持 Windows、MacOS、Linu…