[Leetcode - Array] Combination Sum

本文详细解析了组合总和问题的不同变种,包括允许无限次重复选取候选数、限制每种候选数仅能使用一次、限定组合中元素的数量以及求解组合数目的问题。通过示例和代码展示了如何运用递归回溯和动态规划解决这些经典问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Combination Sum
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

The same repeated number may be chosen from C unlimited number of times.

Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.

For example, given candidate set [2, 3, 6, 7] and target 7, 
A solution set is: 
[
  [7],
  [2, 2, 3]
]

1.解题思路
对数组进行排序,每次加入第i个数后,target就减去这个数,并作为新的target,进行递归。如果target<0,则说明本次无解;如果target=0,则将本序列组合加入结果集中。因为数字可以repeated,所以每次递归都从第i个开始。
2.代码

public class Solution {
    List<List<Integer>> res=new ArrayList<List<Integer>>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        if(candidates.length==0) return res;
        Arrays.sort(candidates);
        combine(candidates,target,0,new ArrayList<Integer>());
        return res;
    }
    private void combine(int[] candidates, int target, int index,List<Integer> pre){
         if(target<0) return;
         if(target==0){
             res.add(pre);
             return;
         }
         for(int i=index;i<candidates.length;i++){
             List<Integer> cur=new ArrayList<Integer>(pre);
             cur.add(candidates[i]);
             combine(candidates,target-candidates[i],i,cur);
             
         }
            
    }
}

Combination Sum II
Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

Each number in C may only be used once in the combination.

Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.

For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8, 

  A solution set is: 
  [
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

1.解题思路
本题与上一题的区别就是同一个数字在一个组合中只能出现一次,那么就需要考虑下面两种情况:
1) 每次递归开始的index要加1;
2)在同一个for循环中,即针对同一个target,需要排除重复的数字。因为数组已经事先进行排序,所以只要判断下当前数字是否和前一个数字相等即可。
2.代码

public class Solution {
    List<List<Integer>> res=new ArrayList<List<Integer>>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        if(candidates.length==0) return res;
        Arrays.sort(candidates);
        combineHelper(candidates,0,target,new ArrayList<Integer>());
        return res;
    }
    private void combineHelper(int[] candidates,int index,int target,List<Integer> pre){
        if(target<0) return;
        if(target==0){
            res.add(pre);
            return;
        }
        for(int i=index;i<candidates.length;i++){
            if(i>index&&candidates[i]==candidates[i-1]) continue;
            List<Integer> cur=new ArrayList<Integer>(pre);
            cur.add(candidates[i]);
            combineHelper(candidates,i+1,target-candidates[i],cur);
                
            
        }
    }
}

Combination Sum III
Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.

Example 1:

Input: k = 3, n = 7

Output:

[[1,2,4]]

Example 2:

Input: k = 3, n = 9

Output:

[[1,2,6], [1,3,5], [2,3,4]]

1.解题思路

其实本题与题I非常相似,只是加入了组合元素的个数限制k。题意其实就是从数组[1,2,...,9]中选出k个数,使得sum等于target。唯独一点与题I的区别就是这里每个数只能选一遍,所以每次递归都从i+1开始。

2.代码

public class Solution {
    List<List<Integer>> res =new ArrayList<List<Integer>>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        if(k==0||n==0) return res;
        int[] nums={1,2,3,4,5,6,7,8,9};
        combine(nums,0,n,k,new ArrayList<Integer>());
        return res;
    }
    private void combine(int[] nums,int index,int target,int k,List<Integer> pre){
        if(target<0) return;
        if(target==0&&k==0){
            res.add(pre);
            return;
        }
        for(int i=index;i<nums.length;i++){
            List<Integer> cur=new ArrayList<Integer>(pre);
            cur.add(nums[i]);
            combine(nums,i+1,target-nums[i],k-1,cur);
        }
    }
}

Combination Sum IV
Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.

Example:

nums = [1, 2, 3]
target = 4

The possible combination ways are:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)

Note that different sequences are counted as different combinations.

Therefore the output is 7.

1.解题思路
受前面三题的启发,很容易就想到继续用递归进行回溯,但是发现Time Exceed Limit. 再仔细看题目,发现其实是动态规划。给定一个数组,求和为target的组合个数,那我们定义状态dp[i]表示和为i的数字组合的个数,那么状态转移就是dp[i]=dp[i-nums[0]]+dp[i-nums[1]]+...+dp[i-nums[n-1]];当然前提是i大于nums[]。
其实说到这,我们发现这其实和动态规划的经典题目CoinChange找零钱非常相似,但找零钱相对复杂一些,因为找零钱需要得到所有组合中拥有最少元素的组合的元素数量,即最少的纸币数目;但这一题只需要求出所有的组合数即可。
CoinChange可参考 https://segmentfault.com/a/11...

2.代码

public class Solution {
    public int combinationSum4(int[] nums, int target) {
        if(nums.length==0||target==0) return 0;
        int[] dp=new int[target+1];
        dp[0]=1;
        for(int i=1;i<=target;i++){
           for(int n:nums){
               if(i>=n){
                   dp[i]+=dp[i-n];
               }
           }
        }
        return dp[target];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值