原题链接:https://leetcode.com/problems/combination-sum-iii/
我在github上的leetcode仓库:https://github.com/cooljacket/leetcodes
题意
给定数字的个数k和总和n,要求从[1-9]中选取k个各不相同的数字组合,其总和刚好等于n,返回所有组合情况
思路
无脑DFS,即使遍历所有情况,也就是O(n^k)。
想象一棵9叉树,它是满的,并且刚好有k层。
优化:
1. 我们不必DFS到第k层,到k-1层就够了。因为知道总和,又知道前k-1个数字的和,自然知道最后一个值应该是多少,不必再枚举了!
2. 在枚举每一层的时候,也是可以剪枝的,比如当前填进去的数字是x,那么接下来要填的数字至少是x+1, x+2, …,假设我们有num个数字要填(包括x),那么它们的和就是:
sum(x,x+1,x+2,...x+num−1)=(x+x+num−1)∗num/2
如果这个总和加上当前的和,超过n的话,就不必往下遍历了,因为后面都是非法的,这已经是总和最少的情况了,还比n大!
代码
class Solution {
public:
vector<vector<int> > combinationSum3(int k, int n) {
vector<vector<int> > ans;
vector<int> v;
dfs(0, 0, k, n, v, ans);
return ans;
}
private:
void dfs(int last, int sum, int k, int n, vector<int>& v, vector<vector<int> >& ans) {
if (k - v.size() == 1) {
int key = n - sum;
if (key > last && key <= 9) {
v.push_back(key);
ans.push_back(v);
v.pop_back();
}
return;
}
for (int i = last+1; i <= 9; ++i) {
int numToFill = k - v.size();
// 注意后半部分的表达式,其实是计算等差数列的公式: sum(a, a+1, ..., a+num-1)
// futureSum表示如果要将i作为下一个的话,未来的和至少是多少!以此来剪枝
int futureSum = sum + ((i + i + numToFill - 1) * numToFill) / 2;
if (futureSum > n)
break;
v.push_back(i);
dfs(i, sum + i, k, n, v, ans);
v.pop_back();
}
}
};