377. 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.
思路
题目是典型的背包问题,就是给你不同面值的硬币,问你用这些硬币凑出某个金额一共有多少种方案,在这个问题里每个面值的硬币都有多个,不限制使用次数。背包问题在学习动态规划的第一堂课上就已经提及到了,但我还没动手做过相关的题目,只是有个概念,前几周做的都是其他类型动态规划的题目,所以这周就去尝试了一下。
应该说背包问题不愧是经典中的经典吗,并没有花费太多的时间和精力就把解答做出来了。这么简单的问题本来不打算拿来写博客的,由于之前我都是用比较熟练的C++来写算法作业的,最近在学Python,想着趁此机会就拿这道题来练练手吧。
还是先说一下题目的解答吧。状态转移方程为:
for i from 0 to target
for j from 0 to nums.size() - 1
dp[i] = dp[i] + dp[i - nums[j]]
其中target为需要求的目标数值,nums数组存储各个可用的数字
状态转移方程非常容易理解,我就不再多加解释了。这个背包问题还有一个变种就是对每个数字的使用有次数限制(一般大于1次),这个就变得有麻烦了,需要再加一个使用次数的循环,我在Leetcode上找不到这个变种题目,所以这次只能做没有次数限制的背包问题。
- C++的解答
class Solution {
public:
int combinationSum4(vector<int>& nums, int target)
{
int ans = 0;
int *dp = new int[target + 1];
dp[0] = 1;
for (int i = 1; i <= target; i++)
{
dp[i] = 0;
for (int j = 0; j < nums.size(); j++)
if (nums[j] <= i)
{
dp[i] += dp[i - nums[j]];
}
}
ans = dp[target];
delete []dp;
return ans;
}
};
- Python的解答
class Solution:
def combinationSum4(self, nums, target):
dp = [0] * (target + 1)
dp[0] = 1
for i in range(1, target + 1):
dp[i] = 0
for num in nums:
if (num <= i):
dp[i] = dp[i] + dp[i - num]
print(dp)
这样一对比就很直观了,像Python这样的脚本语言写出来的代码行数明显就要比c++和java这些常见的面向对象的语言要少,主要是省略了繁琐的数据类型声明和转换步骤。但代码行数少并不能当作评判语言优劣的唯一标准,可以看一下下面这一张图,是我C++跟Python的提交记录。
我们可以清楚地看到两次提交的执行时间有明显差异,但它们执行的步骤是完全相同的,可见对于同一个算法实现,不同语言也是有差异的,有时候还非常明显,主要还是看语言本身的特性。有时候在考虑算法时不能局限于数学层面上的差异,算法效率是受许许多多的因素影响的,实现语言的选择就是其中一个,这也是为什么我要拿这么简单的背包问题写这周博客的原因。当视野变得狭隘时,思想就容易钻牛角尖。