算法设计与分析(十二)

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的提交记录。
对比
我们可以清楚地看到两次提交的执行时间有明显差异,但它们执行的步骤是完全相同的,可见对于同一个算法实现,不同语言也是有差异的,有时候还非常明显,主要还是看语言本身的特性。有时候在考虑算法时不能局限于数学层面上的差异,算法效率是受许许多多的因素影响的,实现语言的选择就是其中一个,这也是为什么我要拿这么简单的背包问题写这周博客的原因。当视野变得狭隘时,思想就容易钻牛角尖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值