diff --git a/notes/src/combinations.md b/notes/src/combinations.md index 7d8a0e9..7b0c8cd 100644 --- a/notes/src/combinations.md +++ b/notes/src/combinations.md @@ -96,4 +96,34 @@ Output: 正确的逻辑应该是如果 `candidates[i] == candidates[i - 1]` 且 `candidates[i - 1]` 使用过,则剪枝。 -怎么判断 `candidates[i - 1]` 是否使用过呢?我们创建一个 `vector used` 用来记录每个元素是否使用过。 +![demo](https://paste.sainnhe.dev/DMfz.png) + +那么我们现在要来定义一下什么叫“使用过”。这张图里面有两种“使用过”,第一种使用过是“在树枝上使用过”,第二种使用过是“在数层上使用过”。 + +第一种“使用过”显然是合法的,我们允许元素在一条树枝上重复出现。而第二种“使用过”是不合法的,生成的结果重复了。 + +因此我们只需要对第二种“使用过”进行剪枝,而保留第一种“使用过”。 + +怎么做呢?我们创建一个 `vector used` 用来记录元素是否在树枝上出现过,初始化为 `false`。 + +```cpp +used[i] = true; +combinationSum2DFS(candidates, target, i + 1, path, sum + candidates[i], + used, result); +used[i] = false; +``` + +那么 `used[i - 1] == true` 说明 `candidates[i - 1]` 在树枝上出现过,我们需要保留这种情况,不剪枝。 + +```cpp +// 剪枝,但保留树枝重复的情况 +if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) + continue; +``` + +另外需要注意一点,为了进行剪枝,我们需要对 `candidates` 进行排序: + +```cpp +// 对 candidates 进行升序排序,这是为了进行剪枝 +sort(candidates.begin(), candidates.end()); +``` diff --git a/src/s0040_combination_sum_ii.cpp b/src/s0040_combination_sum_ii.cpp index 604cb5b..d22c388 100644 --- a/src/s0040_combination_sum_ii.cpp +++ b/src/s0040_combination_sum_ii.cpp @@ -14,7 +14,7 @@ void combinationSum2DFS(vector &candidates, int target, int startIndex, for (int i = startIndex; i < size; ++i) { // 剪枝,当现在节点的 sum 已经超过了 target,就没必要继续迭代了 if (sum + candidates[i] > target) break; - // 剪枝,要对同一树层使用过的元素进行跳过 + // 剪枝,但保留树枝重复的情况 if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) continue; path.push_back(candidates[i]);