跟着Carl学算法--回溯【2】

IP复原(难)

力扣链接:IP复原

题目:有效 IP 地址 正好由四个整数(每个整数位于 0255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

  • 例如:"0.1.2.201" "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245""192.168.1.312""192.168@1.1" 是 无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

思路
回溯三问{当前操作?枚举分隔符‘.’的位置(有三种情况,长度1,2,3)子问题?从下标≥j的后缀中继续修复IP下一个子问题?从下标≥j+1的后缀中继续修复IP回溯三问\left\{\begin{array}{l}\text {当前操作?枚举分隔符‘.’的位置(有三种情况,长度1,2,3)} \\ \text {子问题?从下标} \geq j \text {的后缀中继续修复IP} \\ \text {下一个子问题?从下标} \geq j+1\text {的后缀中继续修复IP}\end{array}\right. 回溯三问当前操作?枚举分隔符‘.’的位置(有三种情况,长度123子问题?从下标j的后缀中继续修复IP下一个子问题?从下标j+1的后缀中继续修复IP
判断IP合法,即所枚举分割的字符串(从i开始,长度枚举)

  1. 开头不为0(注意,这里是当长度大于1才有的约束,单单一个0完全可以,01就不行)
  2. 转换成的数字小于等于255

边界情况

最后可能剩余长度不到3了

比如长度为4 ,i为3,j只能为1,即i+j>n会越界

注意这里:substr(i,j),这里从下标为i的开始,长度为j,i为3说明前面有三个元素,所以i+j>n时才越界,而不是i+j≥n就越界

这里使用了vector<string>来定义路径,而不是string,优点有两个

  • 可以在最后统一处理“.”,而不是每次加入path时就+”.“,不需要在最后一步额外再处理”.“
  • 回溯方便,pop()即可,不需要使用earse函数

stoi(),C++内置字符串转整型

class Solution {
public:vector<string> restoreIpAddresses(string s) {int n = s.size();vector<string> result;vector<string> path;if (n < 4 || n > 12)return {};auto dfs = [&](this auto&& dfs, int i) {if (i == n && path.size() == 4) {result.emplace_back(path[0] + '.' + path[1] + '.' + path[2] +'.' + path[3]);return;}// 剪枝函数,如果剩余字符数小于剩余段数,或者剩余字符数大于剩余段数乘以3,则不可能构成有效的IP地址if (n - i < 4 - path.size() || n - i > 3 * (4 - path.size()))return;for (int j = 1; j <= 3; j++) {if (i + j >n)break;string sub = s.substr(i, j);if (sub[0] == '0' && j > 1) //continue;if (std::stoi(sub) > 255)continue;path.emplace_back(sub);dfs(i + j);path.pop_back();}};dfs(0);return result;}
};

全排列

力扣链接:全排列

题目:给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

思路
回溯三问{当前操作?枚举nums中未使用过的元素子问题?构造path的≥i+1的部分,剩余元素为{nums}-{x1}下一个子问题?继续构造path的≥i+1的部分,剩余元素为{nums}-{x2}回溯三问\left\{\begin{array}{l}\text {当前操作?枚举nums中未使用过的元素} \\ \text {子问题?构造path的≥i+1的部分,剩余元素为\{nums\}-\{x1\}} \\ \text {下一个子问题?继续构造path的≥i+1的部分,剩余元素为\{nums\}-\{x2\}}\end{array}\right. 回溯三问当前操作?枚举nums中未使用过的元素子问题?构造path≥i+1的部分,剩余元素为{nums}-{x1}下一个子问题?继续构造path≥i+1的部分,剩余元素为{nums}-{x2}
image-20250304194326082

与之前的组合不同,这道题可以往回遍历,比如【1,2,3】,当遍历到元素2时,只要元素1未使用就可以接着添加到中,【1,2】,【2,1】是不同的答案,因此整体的不同之处(从答案的角度看)

  • 每次选择路径元素都要将nums整体从头遍历一遍

  • 需要一个used保存已经使用过的元素,保证从头遍历时不会出现重复

如何选择去重集合used,直接使用used结合去重?自定义标记使用元素

错误写法,使用增强for循环时只能遍历,不能修改集合,迭代器指针指向会出现问题

for (int i:unused) { //for增强循环path.push_back(i);unused.erase(i);//修改集合dfs();unused.insert(i);//修改集合
}

现在就存在了问题,要遍历就不能实现回溯,不能同时实现

所以:只能使用自定义标记 bool数组false或true区别

class Solution {
public:vector<vector<int>> permute(vector<int>& nums) {vector<int> path;vector<vector<int>> result;bool used[6] = {false};int n = nums.size();auto dfs = [&](this auto&& dfs, int i) {if (i == n) {result.push_back(path);return;}for (int j = 0; j < n; j++) {if (used[j]) //使用过的元素不在使用continue;path.push_back(nums[j]);used[j] = true;dfs(i + 1);//回溯时记得连同使用过的元素一起回溯path.pop_back();used[j] = false;}};dfs(0);return result;}
};

全排列II

力扣链接:全排列II

题目:给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

相较于全排列nums中有重复元素了
回溯三问{当前操作?枚举nums中未使用过,且本层未使用过的元素子问题?构造path的≥i+1的部分,剩余元素为{nums}-{x1}下一个子问题?继续构造path的≥i+1的部分,剩余元素为{nums}-{x2}回溯三问\left\{\begin{array}{l}\text {当前操作?枚举nums中未使用过,\textcolor{red}{且本层未使用过的元素}} \\ \text {子问题?构造path的≥i+1的部分,剩余元素为\{nums\}-\{x1\}} \\ \text {下一个子问题?继续构造path的≥i+1的部分,剩余元素为\{nums\}-\{x2\}}\end{array}\right. 回溯三问当前操作?枚举nums中未使用过,且本层未使用过的元素子问题?构造path≥i+1的部分,剩余元素为{nums}-{x1}下一个子问题?继续构造path≥i+1的部分,剩余元素为{nums}-{x2}
思路

思考:上一道题与非递减子序列的结合

只需要在上一道题的基础上加个本层去重即可,

应该​排序​+​相同​元素​跳过​或者​每​层​s​et​集合去重均可🤔

这里使用set集合去重

即bool数组用于避免同一个元素使用两次,set集合用于避免出现同一种排列(本层同一种(不是同一个元素选两次)

class Solution {
public:vector<vector<int>> permuteUnique(vector<int>& nums) {vector<vector<int>> result;vector<int> path;bool used[8] = {false}; // 此元素使用过int n = nums.size();auto dfs = [&](this auto&& dfs, int i) {if (i == n) {result.push_back(path);return;}unordered_set<int> curUsed; // 相等元素本层使用过for (int j = 0; j < n; j++) {if (used[j] || curUsed.find(nums[j]) != curUsed.end())continue;path.push_back(nums[j]);used[j] = true;curUsed.insert(nums[j]);dfs(i + 1);//回溯,因为set记录的是本层的重复元素,所以不需要回溯path.pop_back();used[j] = false;}};dfs(0);return result;}
};

N皇后

力扣链接:N皇后

题目:按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

img

思路

与全排列类似,是枚举列号的全排列,举例,即[1,2,3,4]的全排列,数组的下标代表行号,内容代表列号,

在此基础上加了两个难点

  1. 最终结果的输出[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
    • 遍历保存了每一行queen位置的queens数组,queen在第几列,就构造一个n个"."的字符串,同时构造一个n-1-queen(加起来一共是n个字符)位置个“.”字符串,两者通过一个Q进行拼接,即为答案
  2. 每一组全排列要额外满足不在对角线上
    • 很巧妙很巧妙,复制的灵神的
    • 首先对角线分为两类,主对角线和副对角线,需要构造两个数组分别存储
    • 其次,对于同一条对角线,
      • 副对角线(左下到右上):行数 +列数(r+c)相等,想象一下,r每-1,c就+1。
        范围【0,n*2-2】,因为r【0,n-1】,c【0,n-1】,两者相加的范围就是【0,2*n-2】
      • 主对角线(左上到右下):行数 - 列数(r-c)相等,因为,同一条对角线上 r每+1,c就+1。
        范围【-(n-1),n-1】,因为r【0,n-1】,-c【-(n-1),0】,两者相加的范围就是【-(n-1),n-1】
    • 基于以上思考,可以两者均定义为n*2-1的数组,副对角线计算时直接r+c即可,主对角线计算时为避免负索引,需要r-c+n-1

处理完这两个难点,这道题就和全排列的解决步骤相同了

class Solution {
public:vector<vector<string>> solveNQueens(int n) {vector<vector<string>> ans;vector<int> queens(n); // queens 数组用于存储皇后的位置,queens[r] 表示第 r 行的皇后放在了第 queens[r] 列//这里标记位置的col,diag本来bool类型即可,但是灵神说int效率更高vector<int> col(n);// col 数组用于标记每一列是否已经被占用vector<int> diag1(n * 2 - 1); // diag1 数组用于标记副对角线(左下到右上)是否已经被占用// r + c 的值在同一条副对角线上是相同的vector<int> diag2(n * 2 - 1); // diag2 数组用于标记主对角线(左上到右下)是否已经被占用// r - c + n - 1 的值在同一条主对角线上是相同的auto dfs = [&](this auto&& dfs, int r) {//结束if (r == n) {//将queens数组生成字符串,并整合为答案vector<string> board(n);for (int i = 0; i < n; i++) {// 使用字符串构造函数生成一行,先生成 queens[i] 个 '.',然后是 'Q',最后是 n - 1 - queens[i] 个 '.board[i] = string(queens[i], '.') + 'Q' + string(n - 1 - queens[i], '.');}ans.push_back(board);return;}// 在第 r 行的每一列尝试放置皇后for (int c = 0; c < n; c++) {// 计算当前位置所在的副对角线的索引int rc = r - c + n - 1;// 如果当前列和两条对角线都没有被占用,说明可以在当前位置放置皇后if (!col[c] && !diag1[r + c] && !diag2[rc]) { //!col[c]即为与全排列的元素不能重复的逻辑实现queens[r] = c;// 标记当前列和两条对角线已经被占用col[c] = diag1[r + c] = diag2[rc] = true;// 递归调用 dfs,尝试在下一行放置皇后dfs(r + 1);// 恢复现场,回溯到上一步,尝试在其他位置放置皇后col[c] = diag1[r + c] = diag2[rc] = false;}}};dfs(0);return ans;}
};

解数独

力扣链接:解数独

题目:编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 '.' 表示。

image-20250305194744742

思路

难点:

  1. 当前位置枚举数字的合法性检测
  2. 二维递归

合法性检测:3X3范围内只出现一次?

可以先将范围定位到就九个中棋盘,即row/3定位到纵向三个位置中的一个,同理col也是,0,即代表前面有0个粗化的行(即3个细行),其他同理
定位到中位置后,再进一步将大体位置细化到具体的起始行和列,将原来的大体位置*3即可,因为一个行号或者列号对应三个细化的行和列,

以行为例,【0,1,2】分别即对应【0,3,6】

然后知道起始行也知道长度,遍历九宫格即可

for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复for (int j = startCol; j < startCol + 3; j++) {if (board[i][j] == val) {return false;}}}

二维递归

要始终明白,下一个子问题是从填充后面所有位置,而不是填充从下一行开始到结束的所有位置

名称一样二维递归

auto dfs = [&](this auto&& dfs, int row) -> bool {for(int i = row; i < 9; i++)  //(上一层填充了一个位置)从当前行开始重新遍历,更好的做法是直接定位到某行某列,//但是不易于实现和理解for (int j = 0; j < 9; j++) {if (board[i][j] == '.') {for (char num = '1'; num <= '9'; num++) {if (isValid(i, j, num, board)) {board[i][j] = num;if (dfs(i))  //深入判断,只有此处一直为真才会返回真,//当路径偏差时(即上一层为真,但是导致下一层所有数字都不可填入时),会返回假return true;board[i][j] = '.';}}//无论前面是否填充,进行到这一步就返回false,//当前面九个数字都不满足时返回 false//一旦有数字满足就会深入分支,进行进一步深入判断,return false;}}//遍历结束,直接返回真即可,只要遍历到最后,说明前面都符合,//中途一个位置出现了所有数字都不能填充当前位置时(即填充行为无法进行时),就会return falsereturn true;};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914735.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch生成式人工智能(17)——变分自编码器详解与实现

PyTorch生成式人工智能(17)——变分自编码器详解与实现 0. 前言1. 潜空间运算2. 变分自编码器2.1 自编码器与变分自编码器对比2.2 模型训练流程3. 构建变分自编码器3.1 模型构建3.2 模型训练3.3 生成图像4. 向量运算小结系列链接0. 前言 虽然自编码器 (AutoEncoder, AE) 在重…

SpringMVC2

一、接口声明的稳定性- 接口声明不能轻易变&#xff1a;接口是前后端、服务间通信的约定。要是接口的 URL、请求方法、参数、返回值变了&#xff0c;调用方&#xff08;比如前端、其他服务&#xff09;就得跟着改&#xff0c;容易出问题。所以设计接口要谨慎&#xff0c;别老变…

LVS集群实践

一、LVS概念VS: Virtual Sever &#xff08;调度器&#xff09;RS: Real Sever &#xff08;资源主机&#xff09;CIP: Client IP &#xff08;用户IP&#xff09;VIP: Virtual sever IP &#xff08;VS外网的IP&#xff0c;客户访问的IP&#xff09;DIP: Director IP &#xf…

使用Django框架构建Python Web应用

前言Django个高级Python Web框架&#xff0c;遵循MTV&#xff08;Model-Template-View&#xff09;设计模式&#xff1a;模型(Model)&#xff1a;数据层&#xff0c;定义数据结构模板(Template)&#xff1a;表现层&#xff0c;处理用户界面视图(View)&#xff1a;业务逻辑层&am…

[AI-video] 数据模型与架构 | LLM集成

第五章&#xff1a;数据模型与架构 欢迎来到第五章&#xff01; 在前几章中&#xff0c;我们学习了网页用户界面&#xff08;UI&#xff09;&#xff08;控制面板&#xff09;、应用配置&#xff08;系统参数设置&#xff09;、任务编排&#xff08;视频生成流程的总调度&…

HTTP 性能优化实战:突破高并发瓶颈的工业级方案

在互联网高并发场景中&#xff0c;HTTP 性能表现直接决定系统生死。当每秒请求量突破十万级甚至百万级时&#xff0c;哪怕 100 毫秒的延迟都会引发用户流失、交易失败等连锁反应。本文基于五大行业实战案例&#xff0c;拆解 HTTP 性能瓶颈的底层逻辑&#xff0c;输出可直接落地…

Xsens人形机器人拟人动作AI训练,提升机器人工作精度与效率

随着人工智能与机器人技术的深度融合&#xff0c;人形机器人正从实验室走向工业制造、医疗护理、公共服务等真实场景。然而&#xff0c;要让机器人真正"像人类一样工作"&#xff0c;其动作的流畅性、精准度与环境适应性仍是技术突破的关键。Xsens动作捕捉系统通过创新…

IIS网站间歇性打不开暴力解决方法

背景 网站使用 Asp.NET 框架开发&#xff0c;使用 SQL Server 2012 IIS 8.5 运行。开发上线以后&#xff0c;经常出现网站间歇性打不开&#xff0c;但是重启 IIS 就可以正常访问。 问题排查过程 打开日志记录 观察 CPU&#xff0c;内存&#xff0c;带宽流量等占用正常&#xf…

JavaScript 动态访问嵌套对象属性问题记录

问题描述不能解析 2 层 只能解析一层在 Vue 项目中&#xff0c;尝试通过动态路径&#xff08;如 otherInfo.businessPlacePhotoUrlLabel&#xff09;访问或修改嵌套对象属性时&#xff0c;发现 this[a.b.c] 无法正确解析&#xff0c;导致返回 undefined。错误示例removeImg(val…

7.17 滑动窗口 | assign

lc3015.法1&#xff1a;暴力bfs&#xff0c;数据范围only 100&#xff0c;可以过法2&#xff1a;加入了x,y&#xff0c;可以思考加入的x,y影响了什么呢? 通过数学找规律class Solution { public:vector<int> countOfPairs(int n, int x, int y) {vector<int> ret(…

预训练模型:大规模数据预学习范式——定义、原理与演进逻辑

本文由「大千AI助手」原创发布&#xff0c;专注用真话讲AI&#xff0c;回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我&#xff0c;一起撕掉过度包装&#xff0c;学习真实的AI技术&#xff01; 以下基于权威教材、学术论文及行业技术报告&#xff0c;对“预训练模型…

【kubernetes】--安全认证机制

文章目录安全认证1. **身份认证&#xff08;Authentication&#xff09;**2. **授权&#xff08;Authorization&#xff09;**3. **准入控制&#xff08;Admission Control&#xff09;**4. **机密信息管理**5. **其他安全实践**安全认证 Kubernetes 的安全机制覆盖了从身份验…

扣子工作流详解

《扣子开发AI Agent智能体应用&#xff08;人工智能技术丛书&#xff09;》(宋立桓&#xff0c;王东健&#xff0c;陈铭毅&#xff0c;程东升)【摘要 书评 试读】- 京东图书 《扣子开发AI Agent智能体应用》案例重现 开发agent智能体的书籍-CSDN博客 工作流是指一系列相互关联…

【一文解决】块级元素,行内元素,行内块元素

块级元素&#xff0c;行内元素&#xff0c;行内块元素&#xff01;盒模型1.标准盒模型&#xff08;box-sizing: content-box&#xff09;2.IE 盒模型&#xff08;box-sizing: border-box&#xff09;&#xff01;margin & padding1.margin、padding是什么2. 应用一、块级元…

在 Spring Boot 中使用 MyBatis 的 XML 文件编写 SQL 语句详解

前言 在现代 Java Web 开发中&#xff0c;Spring Boot 和 MyBatis 是两个非常流行的技术框架。它们的结合使得数据库操作变得更加简洁和高效。本文将详细介绍如何在 Spring Boot 项目中使用 MyBatis 的 XML 文件来编写 SQL 语句&#xff0c;包括配置、代码结构、SQL 编写技巧以…

字段级权限控制场景中,RBAC与ABAC的性能差异

RBAC(基于角色访问控制)与ABAC(基于属性访问控制)的性能差异主要体现在​​计算复杂度、策略灵活性、扩展性​​和​​资源消耗​​等方面。以下是具体对比分析: ​​一、性能对比维度​​ ​​维度​​​​RBAC​​​​ABAC​​​​计算复杂度​​低(预计算角色权限映射…

Reddit Karma是什么?Post Karma和Comment Karma的提升指南

在Reddit这一用户活跃度高的社区里&#xff0c;想要获得更好的曝光&#xff0c;我们就需要提升我们的Karma值&#xff0c;什么是Reddit Karma&#xff1f;怎么样才能提升以获得更大的影响力&#xff1f;本文将为你提高一套切实可行的提升方案。一、什么是Reddit Karma&#xff…

基于Canal实现MySQL数据库数据同步

一、基础概念与原理 1. Canal是什么&#xff1f; 阿里巴巴开源的MySQL binlog增量订阅与消费组件&#xff0c;通过伪装为MySQL Slave监听Master的binlog变更&#xff0c;实现实时数据同步。 Canal 官方网站&#xff1a;https://github.com/alibaba/canal Canal Demo&#x…

算法第23天|贪心算法:基础理论、分发饼干、摆动序列、最大子序和

今日总结&#xff1a; 摆动序列的三种特殊情况需要着重思考&#xff0c;感觉是没有思考清楚 基础理论 1、贪心的本质&#xff1a; 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 例如&#xff1a;一堆钞票&#xff0c;只能拿走10张&#xff0c;如何拿走最…

Q-chunking——带有动作分块的强化学习:基于人类演示,进行一定的连贯探索(且可做到无偏的n步价值回溯)

前言 我在之前的文章中提到过多次&#xff0c;长沙具身团队是我司建设的第二支具身团队&#xff0c;通过5月份的全力招聘&#xff0c;为了冲刺6月底和7月初来长沙办公室考察的第一批客户&#xff0c;过去一个多月来&#xff0c;长沙分部(一开始就5人&#xff0c;另外5人 实习…