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