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