C++ 回溯法 剪枝 求组合数之和

已知一组数(其中有重复元素),求这组数可以组成的所有子集中,子集中的各个元素和为整数target的子集,结果中无重复的子集。
例如:nums[]=[10,1,2,7,6,1,5], target=8
结果为:[[1,7],[1,2,5],[2,6],[1,1,6]]
该题无论是回溯法或位运算法,整体时间复杂度为O(2^n)。
为了减小时间复杂度,在搜索回溯过程中应进行剪枝操作:
递归调用时,计算已选择元素的和sum,若sum>target,不再进行更深的搜索,直接返回。

#include<vector>
#include<algorithm>
class Solution
{
public:
 Solution() {};
 ~Solution() {};
 std::vector<std::vector<int>> combinationSum2
 (std::vector<int>& candidates, int target)
 {
  std::vector<std::vector<int>> result;
  std::vector<int> item;
  std::sort(candidates.begin(), candidates.end());
  generate(0, candidates, result, item, 0, target);
  return result;
 }
private:
 void generate(int i, std::vector<int>& nums, std::vector<std::vector<int>>& result, std::vector<int> item,
  int sum, int target)
 {
  if (i>=nums.size()||sum>target)
  {
   return;
  }
  sum += nums[i];
  item.push_back(nums[i]);
  if (sum==target&&find(result.begin(),result.end(),item)==result.end())
  {
   result.push_back(item);
  }
  generate(i + 1, nums, result, item, sum, target);
  sum -= nums[i];
  item.pop_back();
  generate(i + 1, nums, result, item, sum, target);
  }
};
int main()
{
 std::vector<int> nums;
 nums.push_back(10);
 nums.push_back(1);
 nums.push_back(2);
 nums.push_back(7);
 nums.push_back(6);
 nums.push_back(1);
 nums.push_back(5);
 std::vector<std::vector<int>> result;
 Solution solve;
 result = solve.combinationSum2(nums, 8);
 for (unsigned int i = 0; i < result.size(); i++)
 {
  if (result[i].size()==0)
  {
   printf("[]");
  }
  for (unsigned int j = 0; j < result[i].size(); j++)
  {
   printf("[%d]", result[i][j]);
  }
  printf("\n");
 }
 return 0;
}

运行结果为:

[1][1][6]
[1][2][5]
[1][7]
[2][6]

此处若将原来的数进行逆序排序后再进行剪枝回溯也可以。
只需将

 std::sort(candidates.begin(), candidates.end());

改为

  std::sort(candidates.rbegin(), candidates.rend());

改为逆序后的运行结果为:

[7][1]
[6][2]
[6][1][1]
[5][2][1]
发布了61 篇原创文章 · 获赞 46 · 访问量 1589

猜你喜欢

转载自blog.csdn.net/weixin_44208324/article/details/104850022
今日推荐