leetcode: Combination Sum III

原题链接: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+num1)=(x+x+num1)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();
        }
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章