原題鏈接: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),那麼它們的和就是:
如果這個總和加上當前的和,超過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();
}
}
};