回溯进阶练习¶
约 2189 个字 405 行代码 3 张图片 预计阅读时间 12 分钟
本篇介绍¶
在回溯基础练习中已经大致了解到回溯解决的问题,并且在题目中也更深刻理解了前面在回溯理论基础提到的回溯法模板,但是这个模板只是为了便于理解回溯算法,并不能将所有题目都强行为了填充模板而限制了思考,所以为了从模板思路跳脱出来,需要考虑下面的练习
力扣1863.找出所有子集的异或总和再求和¶
问题描述:
Quote
一个数组的异或总和定义为数组中所有元素按位XOR
的结果;如果数组为空,则异或总和为0。
例如,数组[2,5,6]
的异或总和为2 XOR 5 XOR 6 = 1
。 给你一个数组nums
,请你求出nums
中每个子集的异或总和,计算并返回这些值相加之和。
注意:在本题中,元素相同的不同子集应多次计数。
数组a
是数组b
的一个子集的前提条件是:从b
删除几个(也可能不删除)元素能够得到a
。
示例 1:
C++ | |
---|---|
1 2 3 4 5 6 7 8 |
|
示例 2:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
示例 3:
C++ | |
---|---|
1 2 3 |
|
思路分析:
本题的基本思路就是求出子集,根据每一个子集求出异或和,最后对所有子集的异或和进行求和,所以基本步骤就是通过变量sum
记录所有子集的异或和之和,xorSum
记录每一个子集的异或和,为了隐藏异或和的回溯,可以考虑在参数部分使用无副作用运算
参考代码:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
力扣22.括号生成¶
问题描述:
Quote
数字n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。
示例 1:
C++ | |
---|---|
1 2 |
|
示例 2:
C++ | |
---|---|
1 2 |
|
思路分析:
本题的基本思路是依次枚举当前位置可以填入的括号,因为是括号对,所以一个位置有两种情况:左括号和右括号,假设有三对括号对,那么需要填充的位置就是6个,根据有效的括号对条件:
- 左括号数量等于右括号数量
- 从第一个括号字符开始,每一个子字符串都满足左括号数量大于等于右括号数量
先枚举左括号,当左括号的个数小于需要的括号对数时,说明还可以添加左括号,否则枚举右括号,当右括号的个数大于等于左括号时,说明右括号不可以再插入。最后,当右括号的个数等于括号对数时,说明已经找到满足要求个数的括号对
参考代码:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
|
力扣494.目标和¶
问题描述:
Quote
给你一个非负整数数组nums
和一个整数target
。
向数组中的每个整数前添加+
或-
,然后串联起所有整数,可以构造一个表达式:
例如,nums = [2, 1]
,可以在2
之前添加+
,在1
之前添加-
,然后串联起来得到表达式+2-1
。 返回可以通过上述方法构造的、运算结果等于target
的不同表达式的数目。
示例 1:
C++ | |
---|---|
1 2 3 4 5 6 7 8 |
|
示例 2:
C++ | |
---|---|
1 2 |
|
思路分析:
本题因为每个数字都有两种情况,所以可以理解为两棵二叉树,第一棵二叉树根节点为1,第二棵二叉树根节点为-1,以此类推,根据符号后会出现两种情况:
- 当前数值为正数,计算和为
sum+nums[i]
- 当前数值为负数,计算和为
sum-nums[i]
参考代码:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
力扣784.字母大小写全排列¶
问题描述:
Quote
给定一个字符串s
,通过将字符串s
中的每个字母转变大小写,我们可以获得一个新的字符串。
返回所有可能得到的字符串集合。以任意顺序返回输出。
示例 1:
C++ | |
---|---|
1 2 |
|
示例 2:
C++ | |
---|---|
1 2 |
|
思路分析:
本题的基本思路是根据是否需要转变大小写分情况进行递归,如果是字母就走两种递归,一种是大写,另外一种是小写,否则就是数字直接走即可
参考代码:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
力扣526.优美的排列¶
问题描述:
Quote
假设有从1到n
的n
个整数。用这些整数构造一个数组perm
(下标从 1 开始),只要满足下述条件之一,该数组就是一个优美的排列:
perm[i]
能够被i
整除i
能够被perm[i]
整除
给你一个整数n
,返回可以构造的优美排列的数量 。
示例 1:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 |
|
示例 2:
C++ | |
---|---|
1 2 |
|
思路分析:
本题的基本思路就是根据优美排列的规则枚举所有可能的情况,但是需要注意,根据给定个数n,第一个位置有n种情况,第二个位置有n-1种情况,以此类推,所以此时就需要涉及到树枝去重,防止在同一个优美排列结果中出现一个数字使用两次的情况。另外,当一个数值无法满足优美排列的条件,就需要枚举后面的值而不是直接跳出循环
参考代码:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
力扣79.单词搜索¶
问题描述:
Quote
给定一个m x n
二维字符网格board
和一个字符串单词word
。如果word
存在于网格中,返回true
;否则,返回false
。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例 1:
C++ | |
---|---|
1 2 |
|
示例 2:
C++ | |
---|---|
1 2 |
|
示例 3:
C++ | |
---|---|
1 2 |
|
思路分析:
本题的基本思路是枚举每一个位置,如果当前位置是需要的字符就继续递归查找下一个,否则直接返回false
,需要注意的是,因为只有第一个字符需要考虑整个字符表,所以只需要在主函数中使用二维数组遍历找出起始字符,其他的只需要根据相邻字符是否存在进行递归查找即可。因为不能重复走一个位置,所以需要used
数组去重
参考代码:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
|
力扣1219.黄金矿工¶
问题描述:
Quote
你要开发一座金矿,地质勘测学家已经探明了这座金矿中的资源分布,并用大小为m * n
的网格grid
进行了标注。每个单元格中的整数就表示这一单元格中的黄金数量;如果该单元格是空的,那么就是0。
为了使收益最大化,矿工需要按以下规则来开采黄金:
- 每当矿工进入一个单元,就会收集该单元格中的所有黄金。
- 矿工每次可以从当前位置向上下左右四个方向走。
- 每个单元格只能被开采(进入)一次。
- 不得开采(进入)黄金数目为 0 的单元格。
- 矿工可以从网格中任意一个有黄金的单元格出发或者是停止。
示例 1:
C++ | |
---|---|
1 2 3 4 5 6 7 |
|
示例 2:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 |
|
思路分析:
本题的基本思路就是固定每一个有效的起点,从该起点出发获取每一条有效路径上的总和,直到求出最大值。因为不能重复走一个位置,所以需要used
数组去重
参考代码:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
力扣980.不同路径Ⅲ¶
问题描述:
Quote
在二维网格grid
上,有4种类型的方格:
- 1表示起始方格。且只有一个起始方格。
- 2表示结束方格,且只有一个结束方格。
- 0表示我们可以走过的空方格。
- -1表示我们无法跨越的障碍。
返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目。
每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。
示例 1:
C++ | |
---|---|
1 2 3 4 5 |
|
示例 2:
C++ | |
---|---|
1 2 3 4 5 6 7 |
|
示例 3:
C++ | |
---|---|
1 2 3 4 5 |
|
思路分析:
本题基本思路就是从起点依次走0的位置,直到找出可以遍历到所有0位置的路径更新计数器即可,需要注意,因为不能重复走一个位置,所以需要使用used
数组去重
参考代码:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
|