【LeetCode】动态规划—377. 组合总和 Ⅳ(附完整Python/C++代码)

题目描述

在这里插入图片描述

前言

组合总和 IV 是一个经典的动态规划问题。给定一个由 正整数 组成的数组 nums 和一个目标值 target,求出可以使用 nums 中的数字通过排列得到目标值 target 的不同组合的数量。数组中的数字可以多次重复使用。

与其他类似的组合问题不同的是,排列的顺序会影响结果。也就是说,对于相同的数字组合,排列不同会被视为不同的组合。


基本思路

1. 问题定义

给定一个正整数数组 nums 和一个目标值 target,求出用 nums 中的数字能组合出目标 target 的不同排列的数量。

举例:

  • 输入:nums = [1, 2, 3], target = 4
  • 输出:7
  • 解释:有 7 种组合可以凑成 4:
    1. [1, 1, 1, 1]
    2. [1, 1, 2]
    3. [1, 2, 1]
    4. [2, 1, 1]
    5. [2, 2]
    6. [1, 3]
    7. [3, 1]

2. 理解问题和递推关系

动态规划思想:

我们可以使用动态规划来解决这个问题。类似于 背包问题,每个数字都可以无限制地使用,但这里的组合顺序会影响结果。

状态定义:

  • dp[i] 表示数字 i 的组合总数。

状态转移方程:

  • 对于每个数字 i,我们可以枚举 nums 中的每个数字 num,如果 num <= i,那么我们可以通过 dp[i - num] 来更新 dp[i],即:
    d p [ i ] + = d p [ i − n u m ] dp[i] += dp[i - num] dp[i]+=dp[inum]
    • dp[i - num] 表示用数字 num 凑成 i 之前已经凑成 i - num 的组合数。

边界条件:

  • dp[0] = 1,表示目标值为 0 的组合数只有 1 种,即不选任何数字。

3. 解决方法

动态规划方法

  1. 初始化:创建一个数组 dpdp[i] 表示可以通过排列组合得到目标值 i 的组合数,dp[0] = 1
  2. 状态转移:对于每一个小于等于目标值的 i,遍历 nums 中的每个数字 num,更新 dp[i]
  3. 返回结果dp[target] 即为最终的组合数量。

伪代码:

initialize dp array with dp[0] = 1
for i from 1 to target:
    for each num in nums:
        if num <= i:
            dp[i] += dp[i - num]
return dp[target]

4. 进一步优化

  • 时间复杂度:时间复杂度为 O(target * n),其中 nnums 数组的长度,target 是目标值。
  • 空间复杂度:空间复杂度为 O(target),因为我们需要存储大小为 target + 1dp 数组。

5. 小总结

  • 递推思路:通过递推 dp 数组,逐步累积到目标值 target。排列的顺序影响最终结果,因此对于每个 i,都要考虑所有可能的排列情况。
  • 时间复杂度:时间复杂度为 O(target * n),适合处理中等规模的输入。

以上就是组合总和 Ⅳ 问题的基本思路。


Python代码

class Solution:
    def combinationSum4(self, nums: list[int], target: int) -> int:
        # 初始化dp数组,dp[i]表示凑成金额i的排列组合数
        dp = [0] * (target + 1)
        dp[0] = 1  # 凑成金额0的方式有1种(不选任何数字)

        # 动态规划计算
        for i in range(1, target + 1):
            for num in nums:
                if num <= i:
                    dp[i] += dp[i - num]
        
        return dp[target]  # 返回凑成目标target的排列组合数

Python代码解释

  1. 初始化:定义 dp 数组,dp[i] 表示凑成金额 i 的排列组合数,初始时 dp[0] = 1
  2. 动态规划递推:遍历每个目标值 i,然后遍历 nums 数组,检查每个数字是否可以组合成 i
  3. 返回结果:最终返回 dp[target],即为凑成 target 的排列组合数。

C++代码

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        // 使用 long long 来避免溢出
        vector<long long> dp(target + 1, 0);
        dp[0] = 1;  // 凑成金额0的方式有1种(不选任何数字)

        // 动态规划计算
        for (int i = 1; i <= target; ++i) {
            for (int num : nums) {
                if (num <= i) {
                    dp[i] += dp[i - num];
                    // 如果 dp[i] 超过 int 范围,停止累加
                    if (dp[i] > INT_MAX) {
                        dp[i] = INT_MAX;  // 将结果限制在 int 范围内
                    }
                }
            }
        }

        // 返回结果,防止溢出返回大于 INT_MAX 的数值
        return dp[target];
    }
};

C++代码解释

  1. 初始化:定义 dp 数组,dp[i] 表示凑成目标值 i 的排列组合数,初始时 dp[0] = 1
  2. 动态规划递推:遍历每个目标值 i,然后遍历 nums 数组,检查每个数字是否可以组合成 i
  3. 返回结果:最终返回 dp[target],即为凑成 target 的排列组合数。

总结

  • 问题核心:本问题是一个典型的动态规划问题,关键在于如何处理每个目标值 i,并考虑不同排列组合的方式。
  • 时间复杂度:时间复杂度为 O(target * n),其中 nnums 数组的长度,target 是目标值。
  • 空间复杂度:空间复杂度为 O(target),因为我们只需要维护一个 dp 数组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Albert_Lsk

今天又能喝柠檬茶啦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值