刷题使我快乐,满脸开心.jpg

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/combination-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目

给你一个 无重复元素 的整数数组candidates和一个目标整数target,找出candidates中可以使数字和为目标数target的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

思路

这个问题虽然是个中等,但是思路其实很常规,直接回溯莽就好,就当成一个每个节点都有数组长度个数的子节点的树即可。

当然,这个树会有自带的一个剪枝策略,那就是元素要小于当前节点的目标值。

candidates = [2,3,6,7], target = 7 为例:

第二个剪枝策略较为隐藏,不过也比较好想出来,那就是去除重复结果,具体实现就是每个节点中,已经选择过的元素,在本节点内后续选择中就不需要再选择了,如图:

只使用第一个剪枝条件,那么就需要后续去重,结果很可能是会超时的,别问我怎么知道的
使用两个剪枝条件后,基本也就趋近于较优解了

代码

双剪枝条件

func combinationSum(candidates []int, target int) [][]int {
	usedNums := make([]int, 0)
	return do(candidates, usedNums, target)
}

func do(candidates, usedNums []int, target int) [][]int {
	res := make([][]int, 0)
	for _, num := range candidates {
		if num > target {
			continue
		}

		used := false
		for _, usedNum := range usedNums {
			if num == usedNum {
				used = true
			}
		}
		if used {
			continue
		}

		if num == target {
			res = append(res, []int{num})
		}

		resTemp := make([]int, 0)
		resTemp = append(resTemp, num)
		resLoop := do(candidates, usedNums, target-num)
		for _, resLoopItem := range resLoop {
			temp := append(resTemp, resLoopItem...)
			res = append(res, temp)
		}
		usedNums = append(usedNums, num)
	}
	return res
}

单剪枝条件

func combinationSum(candidates []int, target int) [][]int {
	res := do(candidates, target)
	trueRes := make([][]int, 0)
	for _, resItem := range res {
		item := resItem
		sort.Ints(item)
		if len(trueRes) <= 0 {
			trueRes = append(trueRes, item)
			continue
		}
		flag := true
		for _, temp := range trueRes {
			if reflect.DeepEqual(item, temp) {
				flag = false
			}
		}
		if flag {
			trueRes = append(trueRes, item)
		}
	}
	return trueRes
}

func do(candidates []int, target int) [][]int {
	res := make([][]int, 0)
	for _, num := range candidates {
		if num == target {
			res = append(res, []int{num})
		}
		if num > target {
			continue
		}

		result := make([]int, 0)
		result = append(result, num)
		resi := do(candidates, target-num)
		for _, item := range resi {
			temp := append(result, item...)
			res = append(res, temp)
		}
	}
	return res
}