找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
没有其他符合的组合了。
示例 3:
输入: k = 4, n = 1
输出: []
解释: 不存在有效的组合。
在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。
这道题跟上一题组合差不多,只是多加了个条件。
建议先做上一个题,上一个题里说过的思路我这就不写了。
继续,回溯三步走:
1. 确定回溯函数条件:
这个就比较容易了,题目所给的n和k肯定得有,然后还得有我们目前所得到的数之和sum,还得有一个记录下标的变量值startindex。startindex是啥,看上一个题。
这次的题目和上一次的有所不同,上一个题给的n是从1到n个数遍历,组合为k个数。
而这道是固定1到9进行遍历,组合为k个数,另加条件为该k个数和为n,所以为了避免混淆,将n的参数定位targetsum。
def backtrack(targetsum,k,startindex,sum):
2.确定终止条件
终止条件就很简单了,如果temp里的个数等于题目所给,则终止,在此之前若temp里的和等于题目所给的值,则先记录到答案集合再终止。
if k == len(temp):
if sum == targetsum:
res.append(temp[:])
return
3.确定循环体
还是利用模板的思路,回调函数之前是本层到下一层要操作的代码,回调函数之后是下一层回到本层要操作的代码。
不用多说,一看就懂。
for i in range(startindex,10):
temp.append(i)
sum +=i
backtrack(targetsum,k,i+1,sum)
temp.pop()
sum -=i
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
res = [] # 存结果
temp = [] # 存路径过程中的数值
def backtrack(targetsum,k,startindex,sum):
# 终止条件
if k == len(temp):
if sum == targetsum:
res.append(temp[:])
return
for i in range(startindex,10):
temp.append(i)
sum +=i
backtrack(targetsum,k,i+1,sum)
temp.pop()
sum -=i
backtrack(n,k,1,0)
return res
第一个可以剪的地方,如果当前temp里的和已经大于目标值了,其实就可以直接return了,即使temp里的个数还没达到k个。
第二个可以剪的地方,就是i的最大遍历起始位置,这一点在上一题中也详细的说过了,这里就不说了,公式: 9 - (k - len(temp)) + 1 + 1
剪枝后的完整代码:
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
#带剪枝
res = [] # 存结果
temp = [] # 存路径过程中的值
def backtrack(target_sum, k, sum, startindex):
# 终止条件
if sum > target_sum: # 剪枝
return
if len(temp) == k:
if sum == target_sum:
res.append(temp[:])
# 循环体
for i in range(startindex, 9 - (k - len(temp)) + 1 + 1): # 剪枝
temp.append(i)
sum += i
backtrack(target_sum, k, sum, i + 1)
temp.pop()
sum -= i
backtrack(n, k, 0, 1)
return res