每日一题:组合总和IV

文章讲述了如何利用动态规划算法解决一个编程问题,即在给定数组中找出总和为目标整数的不同元素组合的个数,通过构建dp数组并更新状态转移方程来实现高效计算。
摘要由CSDN通过智能技术生成

给你一个由 不同 整数组成的数组 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)
请注意,顺序不同的序列被视作不同的组合。

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 1000
  • nums 中的所有元素 互不相同
  • 1 <= target <= 1000

动态规划

目标是找出总和为target的元素组合个数,整个计算过程是累加的过程。因此,想得到target,一定会在计算过程中得到小于target的某个中间数,那么问题就从找到和为target的元素组合个数变成了找到target-x的组合个数。

实际上,由于数组中最小数为1,且在任何位置都有可能出现1,也就是说这里的x可能在任何时刻为1。所以dp[target]最差依赖于dp[target - 1]

因此,设计dp[ i ]表示和为 i 时的组合个数。最终dp[target]即为所求。如上所述,我们需要计算dp[0] 到 dp[target]的每一个值。

状态转移方程:

dp[i] += dp[i - num]

以[1,2,3],target = 4为例,计算过程如下:

  • 首先dp[0] = 1,因为数组中最小值为1,所以只有一个数都不取的时候,和是0,只有一种组合。(对所有情况都成立,边界情况)
  • 计算dp[1]
    遍历数组,num = 1,i = 1。dp[1] += dp[1 - 1],得到dp[1] = 1;
    而当num >= 2 > i = 1时,和已经大于当前target = i了,无需计算。
  • 计算dp[2]
    遍历数组,num = 1 ,i = 2。dp[2] += dp[2 - 1],得到dp[2] = 1;
    继续遍历,num = 2 ,i = 2。dp[2] += dp[2 - 2],得到dp[2] = 2;
    接下来num > i,跳出循环。
  • 计算dp[3]
    num = 1 ,i = 3。dp[3] += dp[3 - 1],得到dp[3] = 2;
    num = 2 ,i = 3。dp[3] += dp[3 - 2],得到dp[3] = 3;
    num = 3 ,i = 3。dp[3] += dp[3 - 3],得到dp[3] = 4;
  • 计算dp[4]
    num = 1 ,i = 4。dp[4] += dp[4 - 1],得到dp[4] = 4;
    num = 2 ,i = 4。dp[4] += dp[4 - 2],得到dp[4] = 6;
    num = 3 ,i = 4。dp[4] += dp[4 - 3],得到dp[4] = 7;
  • 得到结果dp[target] = dp[4]。

在这个过程中,外层循环是dp[target],内层循环是遍历数组。在每次计算target时都遍历数组的每一个数,因而是考虑了不同排列组合的,例如[1,2,1]和[1,1,2]在遍历中是两种情况,前者dp[1] -> dp[3] -> dp[4],后者dp[1] -> dp[2] -> dp[4]。

代码:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值