Leetcode-组合总和IV

这是一篇关于LeetCode中组合总和IV的解题博客,主要讨论了如何将完全背包问题转化为求排列的问题。文章指出,虽然题目表面上求组合,但实际上要求排列,因此在动态规划过程中需要注意遍历顺序。作者详细介绍了动态规划五部曲,并提供了回溯法及记忆化搜索的解题代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.题目描述

给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。

示例:

nums = [1, 2, 3]
target = 4

所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)

请注意,顺序不同的序列被视作不同的组合。

因此输出为 7。

2.题意解析

读完题目,这个题目的本质就是一个完全背包问题。但是这个题目的深层意思就是求排列不是 组合,所以在遍历背包的顺序需要与此前不一样,一般完全背包问题,外层循环是遍历物品,内层循环遍历背包的重量。但是这个是题目对物品的顺序有要求。所以在完全背包的顺序就需要特别注意。具体的做法可以参考上一篇文章目标和。具体的解析可以看下面:

本题题目描述说是求组合,但又说是可以元素相同顺序不同的组合算两个组合,其实就是求排列!

弄清什么是组合,什么是排列很重要。

组合不强调顺序,(1,5)和(5,1)是同一个组合。

排列强调顺序,(1,5)和(5,1)是两个不同的排列。

动态规划五部曲如下:

1.dp数组的定义

dp[i]: 凑成目标正整数为i的排列个数为dp[i].

2.确定dp数组状态转移方程

不考虑nums[j]的情况下,填满容量为j背包,有dp[j]中方法。

那么只要搞到nums[j]的话,凑成j-nums[i]就有dp[j - nums[i]] 种方法。

3.dp数组的初始化

因为递推公式dp[i] += dp[i - nums[j]]的缘故,dp[0]要初始化为1,这样递归其他dp[i]的时候才会有数值基础。

至于dp[0] = 1 有没有意义呢?

其实没有意义,所以我也不去强行解释它的意义了,因为题目中也说了:给定目标值是正整数! 所以dp[0] = 1是没有意义的,仅仅是为了推导递推公式。

至于非0下标的dp[i]应该初始为多少呢?

初始化为0,这样才不会影响dp[i]累加所有的dp[i - nums[j]]。

4.确定遍历顺序

  • 数可以不限使用,说明这是一个完全背包。
  • 得到的集合是排列,说明需要考虑元素之间的顺序。
  • 本题要求的是排列,那么这个for循环嵌套的顺序可以有说法了。

如果求组合数就是外层for循环遍历物品,内层for遍历背包。

如果求排列数就是外层for遍历背包,内层for循环遍历物品。

如果把遍历nums(物品)放在外循环,遍历target的作为内循环的话,举一个例子:计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有{3,1}这样的集合,因为nums遍历放在外层,3只能出现在1后面!

所以本题遍历顺序最终遍历顺序:target(背包)放在外循环,将nums(物品)放在内循环,内循环从前到后遍历。

5.举例来推导dp数组

3.源代码

public int combinationSum4(int[] nums, int target) {
    if (nums == null || nums.length == 0) {
        return 0;
    }
    int[] maximum = new int[target + 1];
    maximum[0] = 1;
    Arrays.sort(nums);
    for (int i = 1; i <= target; i++) {
        for (int j = 0; j < nums.length && nums[j] <= i; j++) {
            maximum[i] += maximum[i - nums[j]];
        }
    }
    return maximum[target];
}

Leetcode 提交:

 回溯法解决代码:

public int combinationSum4_1(int[] nums, int target) {
        if (target == 0) {
            return 1;
        }
        int res = 0;
        for (int num : nums) {
            if (target >= num) {
                res += combinationSum4_1(nums, target - num);
            }
        }
        return res;
    }

上面是暴力DFS 搜索解决方法,效率不是很高,加上记忆化搜索的代码如下:

class Solution {

      int  memory[];
    public int combinationSum4(int[] nums, int target) {
          memory=new int[target+1];
          Arrays.fill(memory,-1);
          memory[0]=1;
          return  dfs(nums,target);
    }

        private int  dfs( int []nums,int target){
            if(memory[target]!=-1){
                  return  memory[target];
            }
             int ans=0;
            for(int  num:nums){
                 if(target>=num){
                       ans+=dfs(nums,target-num);
                 }
            }
          memory[target]=ans;
          return ans;

        }

   
}

Leetcode  提交:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值