算法图解——组合求和( Combination Sum)

 组合求和( Combination Sum)

1. 题目描述

Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order.The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different.It is guaranteed that the number of unique combinations that sum up to target is less than 150 combinations for the given input.

翻译过来就是:【还是建议大家先看英文的自己理解一下再看翻译】

给定一个无重复整数数组candidates 和一个目标整数 target,返回一个唯一的候选组合的列表,其中所选列表中数字之和与目标数相等。返回的列表可以任意顺序。

同一个数可从候选人中无限次选出。如果所选数字中至少一个的频率不同,则两个组合是唯一的。

对于给定的输入,保证总计到目标的唯一组合数少于150个组合。


2. 示例

示例1:

Input: candidates = [2,3,6,7], target = 7
Output: [[2,2,3],[7]]
Explanation:
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
7 is a candidate, and 7 = 7.
These are the only two combinations.

示例2:

Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]

示例3:

Input: candidates = [2], target = 1
Output: []

示例4:

Input: candidates = [1], target = 1
Output: [[1]]

示例5:

Input: candidates = [1], target = 2
Output: [[1,1]]

 

3. 要求

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • All elements of candidates are distinct【数组元素都是不一样的】.
  • 1 <= target <= 500

4. 解题思路 

此题其实类似于背包问题,可以利用贪心的思想。先排序,然后从第一个元素遍历,当把元素 sum[i] 加入到列表 arr 中后(求和列表),剩下的问题其实就是一个子问题——求数组num中和为 target-sum[i] 的子问题。但是 sum[i] 也可能不是求和数组 arr 中的元素,即有了sum[i] 后就不可能组合成target之和。故在递归后,还得需要把该元素给回退掉。

即:

arr.add(nums[i]);  //添加元素
search(res,arr,nums,target-nums[i],i);  //递归
arr.remove(arr.size()-1);//如果不满足的话,还可以退回。

 

5. 实现:

 全文代码实现:

class Solution {
    public List<List<Integer>> combinationSum(int[] nums, int target) {
        ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
        Arrays.sort(nums);//背包问题
        
        search(res,new ArrayList<Integer>(),nums,target,0);
        return res;
    }
    private void search(ArrayList<List<Integer>> res,ArrayList<Integer> arr,int[] nums,int target,int index){
        //待求和为0时
        if(target==0){
            res.add(new ArrayList<Integer>(arr));
            return;
        }
        //待求和<0时返回空。因为1 <= candidates[i] <= 200,且1 <= target <= 500
        if(target<0)  return;
        
        for(int i=index;i<nums.length;i++){
            if(target-nums[i]>=0){
                arr.add(nums[i]);
                search(res,arr,nums,target-nums[i],i);  //递归,注意这里是i 不是i++;
                arr.remove(arr.size()-1);//如果不满足的话,还可以退回。
            }else
                break;
        }
    }
}

 

 

6. 拓展:

题目上说的是求和数组 arr 中的元素可以重复,如示例1,2:

Input: candidates = [2,3,6,7], target = 7
Output: [[2,2,3],[7]]
Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]

那么,如果该要求改为不可以重复,该如何解呢?

Input: candidates = [2,3,6,7], target = 7
Output: [[7]]

Input: candidates
= [2,3,5], target = 8 Output: [[3,5]]

其实,我们只需要在添加元素 nums[i] 到arr 中后,再把原数组nums 中对应的元素给去掉就行,然后形成子问题,再之后再恢复过来即可。

 

 

 

 

 

参考致谢:

1、LeetCode Top 100 高频算法题39. Combination Sum

 

 

Over......

 

posted @ 2021-02-16 19:42  额是无名小卒儿  阅读(1471)  评论(0编辑  收藏  举报