Combination Sum
回溯算法的剪枝非常重要!!
这个题是一个NP问题,方法仍然是N-Queens中介绍的套路。基本思路是先排好序,然后每次递归中把剩下的元素一一加到结果集合中,并且把目标减去加入的元素,然后把剩下元素(包括当前加入的元素)放到下一层递归中解决子问题。算法复杂度因为是NP问题,所以自然是指数量级的。
简单的回溯法(递归实现).
比如对于数组3,2,6,7,target = 7,对数组排序得到[2,3,6,7]
1、第1个数字选取2, 那么接下来就是解决从数组[2,3,6,7]选择数字且target = 7-2 = 5
2、第2个数字选择2,那么接下来就是解决从数组[2,3,6,7]选择数字且target = 5-2 = 3
3、第3个数字选择2,那么接下来就是解决从数组[2,3,6,7]选择数字且target = 3-2 = 1
4、此时target = 1小于数组中的所有数字,失败,回溯,重新选择第3个数字
5、第3个数字选择3,那么接下来就是解决从数组[2,3,6,7]选择数字且target = 3-3 = 0
6、target = 0,找到了一组解,继续回溯寻找其他解。
贴上代码:
#include <iostream> // std::cout #include <algorithm> // std::is_heap_until, std::sort, std::reverse #include <vector> // std::vector using namespace std; class Solution { public: vector<vector<int>> combinationSum(vector<int>& candidates, int target) { vector<int> temp; sort(candidates.begin(), candidates.end()); if (candidates.size() == 0)//还有candidates==NULL的情况 return res; helper(candidates, 0, target, res, temp);// return res; } void show(){ for (int i = 0; i < res.size(); i++) { for (int j = 0; j < res[i].size(); j++) cout << res[i][j] << " "; cout << endl; } } private: void helper(vector<int>& candidates, int index, int target,vector<vector<int>>& res,vector<int>& temp){ if (target < 0) return; if (0 == target){ res.push_back(temp); return; } for (int i = index; i < candidates.size(); i++){ //这儿i=index,千万别再写成了i=0 if (i>0 && candidates[i] == candidates[i - 1]) continue; //if (candidates[i] <= target){//这儿不需要这一句,让其添加就好了,否则target就不会有变为负数的机会了 temp.push_back(candidates[i]); target -= candidates[i]; //helper(candidates, i, target, res, temp);//问题:这个target的值并没有随着递归返回而恢复原来的值 helper(candidates, i, target, res, temp); target += candidates[i]; //这个地方也非常关键 temp.pop_back(); //} } } private: vector<vector<int>> res; }; int main() { vector<int> arr{1 }; Solution test; test.combinationSum(arr, 1); test.show(); return 0; }
手里拿着一把锤子,看什么都像钉子,编程界的锤子应该就是算法了吧!