(关注数据结构和算法,了解更多新知识)
--------------下面是今天的算法题--------------
来看下今天的算法题,这题是LeetCode的第377题:组合总和 Ⅳ。
问题描述
来源:LeetCode第377题
难度:中等
给你一个由不同整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
示例1:
输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。
示例2:
输入:nums = [9], target = 3
输出:0
1 <= nums.length <= 200
1 <= nums[i] <= 1000
nums 中的所有元素互不相同
1 <= target <= 1000
问题分析
这题是组合总和的第4题,至此LeetCode上组合总和的4道题全部都讲完了,前面三道使用的都是回溯算法,而这题如果使用回溯算法肯定会超时,我们可以使用动态规划来解决。
这题求的是排列问题,不是组合问题。我们可以参考爬楼梯这题来解,爬楼梯每次只能上一层或两层,问达到第 n 层有多少种方式。而这题可以理解为每次可以上nums[i]层,到达target层总共有多少种方式,我们只需要枚举nums[i]即可。
这题能不能使用背包问题来解呢?这个是不行的,因为背包问题是组合问题,不需要顺序,比如[1,2]和[2,1]的值是一样的。而这题是排列问题,不同的顺序值是不一样的,比如[1,2]和[2,1]是不同的。其实他和背包问题也有很类似的地方,有兴趣的可以看下《算法秘籍》中第11章的 11.3 组合与排列。
JAVA:
public int combinationSum4(int[] nums, int target) {
// dp[i]表示和为i的组合个数
int[] dp = new int[target + 1];
dp[0] = 1;
for (int i = 1; i <= target; i++)
for (int j = 0; j < nums.length; j++)
if (i - nums[j] >= 0)
dp[i] += dp[i - nums[j]];
return dp[target];
}
C++:
public:
int combinationSum4(vector<int> &nums, int target) {
// dp[i]表示和为i的组合个数
vector<int> dp(target + 1);
dp[0] = 1;
for (int i = 1; i <= target; i++)
for (int &num: nums)
if (i - num >= 0 && dp[i - num] <= INT_MAX - dp[i])
dp[i] += dp[i - num];
return dp[target];
}
C:
int combinationSum4(int *nums, int numsSize, int target) {
// dp[i]表示和为i的组合个数
int dp[target + 1];
memset(dp, 0, sizeof(dp));
dp[0] = 1;
for (int i = 1; i <= target; i++)
for (int j = 0; j < numsSize; j++)
if (i >= nums[j] && dp[i - nums[j]] <= INT_MAX - dp[i])
dp[i] += dp[i - nums[j]];
return dp[target];
}
Python:
def combinationSum4(self, nums: List[int], target: int) -> int:
# dp[i]表示和为i的组合个数
dp = [0] * (target + 1)
dp[0] = 1
for i in range(1, target + 1):
for num in nums:
if i - num >= 0:
dp[i] += dp[i - num]
return dp[target]
笔者简介
博哥,真名:王一博,毕业十多年,《算法秘籍》作者,专注于数据结构和算法的讲解,在全球30多个算法网站中累计做题2000多道,在公众号中写算法题解800多题,对算法题有自己独特的解题思路和解题技巧,喜欢的可以给个关注,也可以下载我整理的1000多页的PDF算法文档。