377. Combination Sum IV

本文探讨了如何求解给定正整数数组中所有可能的组合方式,使其总和等于指定的目标值。通过递归与动态规划两种方法实现,递归方法虽然直观但效率较低;动态规划方法则有效避免了重复计算,显著提高了算法性能。

  • Total Accepted: 33521 
  • Total Submissions: 80346 
  • Difficulty: Medium
  • Contributor: LeetCode

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.

Follow up:
What if negative numbers are allowed in the given array?
How does it change the problem?
What limitation we need to add to the question to allow negative numbers?



解题思路:

要使得某一组合的和为target,我们可以把这个问题分解,假设最后加的一个数为k,那么上一步所得到的和值为target-k,i可以是数组中的任意一个数。

记f(k)为和值为k的组合数,那么f(k) = sum{f(k - nums[i])},当0 < i < nums.size且k ≥ nums[i]。当k=0时,只有一种组合数,就是什么都不选取。

因此,很容易得到这个问题的一个递归解法,代码如下:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
    	int res = 0;
     	if(target == 0)
     		return 1;
     	for(int i = 0; i < nums.size(); i++){
     		if(nums[i] <= target)
     			res += combinationSum4(nums, target - nums[i]);
     	}
     	return res;
    }
};

但是使用递归会进行许多不必要的重复计算,十分费时。使用动态规划效率会更高。考虑到所计算的f(k)中k的最大值不会超过target,因此可以用一个大小为target的数组来储存所有的f(k)值,以避免重复计算。先给数组所有位置赋值-1,在每次需要计算f(k)之前先查看数组a[k]是否为-1,若不是,则说明f(k)已经在之前计算过,只需要使用数组里储存的值。若之前没有计算过,则计算一次,并把结果写入数组。代码如下:

class Solution {
public:
	vector<int> res;

    int comb(vector<int>& nums, int target){
    	if(res[target] != -1)
    		return res[target];
    	int temp = 0;
    	for(int i = 0; i < nums.size(); i++){
     		if(nums[i] <= target)
     			temp += comb(nums, target - nums[i]);
     	}
     	res[target] = temp;
     	return temp;
    }

    int combinationSum4(vector<int>& nums, int target) {
    	int size = target + 1;
    	while(size --)
    		res.push_back(-1);
     	res[0] = 1;
     	return comb(nums, target);
    }
};

然而这种方法是target不断减小的方法,我们还可以采用不断增大target的方法。这样每次需要计算的f(k - nums[i])都一定是在之前已经计算过的,就不需要再考虑判断是否计算过。代码如下:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
     	vector<int> res(target + 1, 0);
     	res[0] = 1;
     	for(int i = 1; i < res.size(); i++){
     		for(int j = 0; j < nums.size(); j++){
     			if(i >= nums[j])
     				res[i] += res[i - nums[j]];
     		}
     	}
     	return res[target];
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值