1. 程式人生 > >(LeetCode 39)組合總和 [DFS: 暴力搜尋 + 剪枝 + 去重]

(LeetCode 39)組合總和 [DFS: 暴力搜尋 + 剪枝 + 去重]

39. 組合總和
給定一個無重複元素的陣列 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和為 target 的組合。

candidates 中的數字可以無限制重複被選取。

說明:

所有數字(包括 target)都是正整數。
解集不能包含重複的組合。
示例 1:

輸入: candidates = [2,3,6,7], target = 7,
所求解集為:
[
[7],
[2,2,3]
]
示例 2:

輸入: candidates = [2,3,5], target = 8,
所求解集為:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

分析:
首先我們可以暴力枚舉出所有的正確組合。但是會存在"重複"問題。如[2,2,3], [3,2,2],[2,3,2]…

如何去重呢?
對於重複的問題,是因為在某一解中我們已經先添加了candidates[i] , 然後再加入candidates[j] (i < j) ; 而在另一個解中是按照:先新增candidates[j] 後 加入 candidates[i] 所造成的。
所以為了防止重複解出現,我們只需規定: 在某一個可行解中,它的所有元素是按照它們在candidates[]中的索引大小順序新增的,即:不能先新增後面的數,再新增前面的數。

我們使用 int used 來表示:當前組合cur[]中,已經使用到了第used個元素,所以之後為了不產生重複解,cur[]只能從candidates[used]開始新增。

dfs函式引數的含義:
cur : 當前試探的組合
sum: 當前組合的和
used: 當前組合cur[]中,已經使用到了第used個元素

優化:剪枝
先將candidates[]排序,若新增某個數後sum > target ,由於後面的數更大,一定也會使得 sum > target, 所以不用考慮,可以直接剪枝掉。

AC程式碼:

class Solution {
public:
    int n;
    void dfs(vector<int> cur, int sum, int used, vector<vector<int>>& ans,vector<int>& candidates,int target)
    {
        if(sum > target) return;
        if(sum == target)
        {
            ans.push_back(cur);
            return;
        }
        for(int i=used;i<n;i++)
        {
            vector<int> t = cur;
            t.push_back(candidates[i]);
            dfs(t,sum+candidates[i],i,ans,candidates,target);
            if(sum+candidates[i] > target) break;
        }
    }
    
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        vector<vector<int>> ans;
        vector<int> cur;
        n = candidates.size();
        dfs(cur,0,0,ans,candidates,target);
        return ans;
    }
};