chapter_backtracking/subset_sum_problem/ #559
Replies: 35 comments 38 replies
-
实在是太有石粒辣,感觉这种问题的难点在于剪枝操作怎么写,遍历递归前需要提前考虑的东西很多 |
Beta Was this translation helpful? Give feedback.
-
根本不是人能想出来的,一环套一环,四环又四环,四环外边是五环。 |
Beta Was this translation helpful? Give feedback.
-
重复元素的情况,剪枝四的 i > start 是不是应该改为 i > 0 啊,不然无法剪枝n个元素之和等于目标值的情况。 还有个疑问是可以排序时去重代替剪枝四吗? |
Beta Was this translation helpful? Give feedback.
-
subset_sum_ii.js
需要调整为: i+1 --> i ,不然不满足每个元素可被选取多次的条件~ |
Beta Was this translation helpful? Give feedback.
-
请教,13.3.2中->“实现方式比较巧妙:由于数组是已排序的,因此相等元素都是相邻的”,题目没有说是给定有序数组nums啊?为什么默认就以有序数组来处理了呢? |
Beta Was this translation helpful? Give feedback.
-
关于重复情况的剪枝4,其实跟全排列问题中的 def backtrack(
state: list[int], target: int, choices: list[int], start: int, res: list[list[int]]
):
"""回溯算法:子集和 II"""
# 子集和等于 target 时,记录解
if target == 0:
res.append(list(state))
return
# 遍历所有选择
# 剪枝二:从 start 开始遍历,避免生成重复子集
# 剪枝三:从 start 开始遍历,避免重复选择同一元素
duplicated = set[int]()
for i in range(start, len(choices)):
# 剪枝一:若子集和超过 target ,则直接结束循环
# 这是因为数组已排序,后边元素更大,子集和一定超过 target
if target - choices[i] < 0:
break
# 剪枝四:如果该元素重复,跳过
if choices[i] in duplicated:
break
# 尝试:做出选择,更新 target, start
state.append(choices[i])
# 进行下一轮选择
backtrack(state, target - choices[i], choices, i + 1, res)
# 回退:撤销选择,恢复到之前的状态
state.pop() 这样可以省去对数组进行排序。我理解的对吗? |
Beta Was this translation helpful? Give feedback.
-
考虑全排列的情况下,我这种改法是否会有问题?和原始的代码只有一个 slicing 的区别。
|
Beta Was this translation helpful? Give feedback.
-
个人愚见: 13.3.2题目:“给定数组包含重复元素,每个元素只可被选择一次”; 是否中间在增加一个案例: |
Beta Was this translation helpful? Give feedback.
-
13.3.2 考虑重复元素的情况 |
Beta Was this translation helpful? Give feedback.
-
老板,出书了嘛??有计划嘛?? |
Beta Was this translation helpful? Give feedback.
-
backtrack(state, target - choices[i], choices, i , res); |
Beta Was this translation helpful? Give feedback.
-
建议大佬 把相似的leetcode题目附上,让大家去练 |
Beta Was this translation helpful? Give feedback.
-
感觉回溯是枚举的另一实现 |
Beta Was this translation helpful? Give feedback.
-
第二项代码的state.pop()换成break会更好一点吗 |
Beta Was this translation helpful? Give feedback.
-
为什么13.3.2中剪枝过程能不像上一份一样用break而改用continue? |
Beta Was this translation helpful? Give feedback.
-
在图 13-14中,感觉第三层的第一个节点不应该出现,因为在第二轮选择中,-4^应该被剪枝4去掉。 |
Beta Was this translation helpful? Give feedback.
-
写了个自认为比较清晰的代码,如有错误,还请指正。 void backtrack(vector<int> &nums, vector<int> ¤t, int total, int target, vector<vector<int>> &result, int index)
{
if (total == target)
{ // 如果当前集合之和等于目标,则将当前集合加入结果集
result.push_back(current);
return;
}
for (int i = 0; i < nums.size(); ++i)
{
if (nums[i] + total > target || i < index || nums[i] == nums[i - 1]) continue;
// 如果超出边界、出现重复遍历以及重复元素,则跳过
current.push_back(nums[i]); // 将当前元素加入集合
backtrack(nums, current, total + nums[i], target, result, i); // 递归调用下一层
current.pop_back(); // 回溯到上一层,移除当前元素
}
} |
Beta Was this translation helpful? Give feedback.
-
重复元素的情况中4种剪枝确实有点复杂,尤其是剪枝二和剪枝三很容易混淆,这两者的维度是不同的,剪枝二是最外层遍历时会逐步减少一个元素,而剪枝三则是往内层遍历时逐步减少一个元素,感觉好像抓住了重点,但是又感觉自己理解的不够深刻,还是需要多看多调试 |
Beta Was this translation helpful? Give feedback.
-
我的理解是这样的: |
Beta Was this translation helpful? Give feedback.
-
其实很简单 剪枝二和剪枝三 第一次看注释为什么都是start开始遍历还有两个结果的 瞬间云里雾里 |
Beta Was this translation helpful? Give feedback.
-
可否在这请教各位佬一道算法题,之前做的没做上来,学到这想想有点像,又有点不一样 |
Beta Was this translation helpful? Give feedback.
-
相等元素既有自身和自身相等,又有排序后相邻相等元素,因此分别对应剪枝3和剪枝4 |
Beta Was this translation helpful? Give feedback.
-
简单分享下自己的理解: 对于第二个问题,由于已将数组排序,大小相同的元素会排列在一起,在每轮选择排除重复元素的方法是判断此选择是否与前一个元素相同,注意剪枝4判断中会有 |
Beta Was this translation helpful? Give feedback.
-
昨天晚上梦见宿管阿姨问我回溯算法,我全说上来了:D |
Beta Was this translation helpful? Give feedback.
-
重复子集那里感觉解释得不太清楚,我理解应该是当做出选择 𝑥𝑖 后,设定下一轮从索引 𝑖 + 1 开始向后遍历和相等元素在每一轮中只能被选择一次共同保证不会出现重复子集 |
Beta Was this translation helpful? Give feedback.
-
感觉重复元素的情况解法和题意应该出现了偏差?题目是说每个元素只可被选择一次,题解是相等元素在每一轮中只能被选择一次,这道题应该是对应 LeetCode 组合总和 2 这道题,需要引入一个 visited 辅助数组来解答。 |
Beta Was this translation helpful? Give feedback.
-
学习笔记,js版 /* k数之和,含重复元素,回溯法,可选择是否允许元素复用 */
function k_sum_backtracking(nums, k, can_reuse = false) {
const res = []
const sub = []
backtrack()
return res
function backtrack(j = 0) {//记录前一轮递归中指针i到达的位置,以实现剪枝不同选择顺序造成的重复子集
const vals = new Set()//记录使用过的值,以实现剪枝元素同值造成的重复子集
if (!k) return res.push([...sub])
for (let i = j; i < nums.length; i++) {
const num = nums[i]
if (k - num < 0 || vals.has(num)) continue
/* 进 */
sub.push(num)
k -= num
vals.add(num)
backtrack(i + !can_reuse)//后一轮递归从i(不允许复用元素时,i+1)位置开始遍历元素
/* 退 */
sub.pop(num)
k += num
}
}
} |
Beta Was this translation helpful? Give feedback.
-
关于重复哪个,为什么不直接数组去重 |
Beta Was this translation helpful? Give feedback.
-
LeetCode 对应题目: 1. 无重复元素:https://leetcode.cn/problems/combination-sum/description/ |
Beta Was this translation helpful? Give feedback.
-
chapter_backtracking/subset_sum_problem/
在动画与代码中掌握数据结构与算法
https://www.hello-algo.com/chapter_backtracking/subset_sum_problem/
Beta Was this translation helpful? Give feedback.
All reactions