NC46 加起来和为目标值的组合

题意分析:

在给定的数组中找到一些子集,这些子集的和等于target

题解一(回溯):

枚举出所有子集,判断这些子集里面的元素和是不是等于target,等于就加入到最后结果中。

代码实现如下:

void backtrace(vector<int>&nums,vector<int>tmp,int index,vector<vector<int>>&ret,int sum,int target){
    //tmp保存生成的组合,sum为生成的组合总和,index指向num数组要添加的位置
    if(sum==target){
        //当target==sum,加入生成的组合
        ret.push_back(tmp);
    }
    for(int i=index;i<nums.size();i++){
        //tmp加入num[i]
        tmp.push_back(nums[i]);
        //开始回溯
        backtrace(nums,tmp,i+1,ret,sum+nums[i],target);
        //回溯结束后,弹出添加的元素
        tmp.pop_back();
    }
}

vector<vector<int> > combinationSum2(vector<int> &num, int target) {

    vector<int>tmp;
    vector<vector<int>>ret;
    vector<vector<int>>final_ret;
    set<vector<int>>s;
    backtrace(num,tmp,0,ret,0,target);
    for(auto i:ret){
        //因为最后结果需要有序,所以需要排序一下
        sort(i.begin(),i.end());
        s.insert(i);
    }
    for(auto i:s){
        final_ret.push_back(i);
    }
    return final_ret;
}

时间复杂度:,我们枚举出了每一个子集,子集的数目一共有个。

时间复杂度:

上述代码不会被ac,时间开销太大。

题解二(回溯+剪枝):

有没有办法减少递归的次数?将给定数组排序后,之后再寻找子集。如果当前的子集和已经大于target,说明后面的元素已经没有再被添加进该子集的必要了,因为再大的元素加进去只会使结果变得更大,因此直接返回即可。

void backtrace(vector<int>&nums,vector<int>tmp,int index,vector<vector<int>>&ret,int sum,int target){
    if(sum==target){
        ret.push_back(tmp);
    }
    if(target<sum){
         //当前加入的元素过大,结束回溯,后面的元素也不必进行试探添加
        return ;
    }
    for(int i=index;i<nums.size();i++){
        tmp.push_back(nums[i]);
        backtrace(nums,tmp,i+1,ret,sum+nums[i],target);
        tmp.pop_back();
    }
}

vector<vector<int> > combinationSum2(vector<int> &num, int target) {

    vector<int>tmp;
    vector<vector<int>>ret;
    vector<vector<int>>final_ret;
    set<vector<int>>s;
    sort(num.begin(),num.end());
    backtrace(num,tmp,0,ret,0,target);
    for(auto i:ret){
        sort(i.begin(),i.end());
        s.insert(i);
    }
    for(auto i:s){
        final_ret.push_back(i);
    }
    return ret;
}

时间复杂度:,虽然我们做了一些剪枝,复杂度降低了,但非确切上界依然是

时间复杂度: