动态规划完全背包问题03:组合总和 Ⅳ

249 阅读3分钟

组合总和 Ⅳ

力扣:377. 组合总和 Ⅳ - 力扣(LeetCode)
给你一个由不同整数组成的数组 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)
请注意,顺序不同的序列被视作不同的组合。
示例 2:
输入: nums = [9], target = 3
输出: 0

看到这题中的一句话:“请注意,顺序不同的序列被视作不同的组合。”相信如果看了我上一篇文章:动态规划完全背包问题02:零钱兑换 II - 掘金 (juejin.cn)的朋友一定倍感熟悉甚至不用继续看文章就已经知道答案代码怎么写了。注意上一题要求的是组合而这一题题⽬描述说是求组合,但⼜说是可以元素相同顺序不同的组合算两个组合,其实就是求排列!没看过上一篇文章的朋友,也不想回去看的也没关系。我们从新开始讲。
首先弄清什么是组合,什么是排列很重要。
组合不强调顺序,(1,5)和(5,1)是同⼀个组合。
排列强调顺序,(1,5)和(5,1)是两个不同的排列。
本题求的是排列总和,⽽且仅仅是求排列总和的个数,并不是把所有的排列都列出来。如果本题要把排列都列出来的话,只能使⽤回溯算法爆搜。回到老本行我们开始动态规划五部曲。

动规五部曲:

1. 确定dp数组以及下标的含义

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

2. 确定递推公式

dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导出来。因为只要得到nums[j],排列个数dp[i - nums[j]],就是dp[i]的⼀部分。
在前面的文章中我们已经详细讲过了,求装满背包有⼏种⽅法,递推公式⼀般都是dp[i] += dp[i - nums[j]];本题也⼀样。

3. dp数组如何初始化

⾸先dp[0]⼀定要为1,dp[0] = 1是 递归公式的基础。 从dp[i]的含义上来讲就是,凑成总⾦额0的货币组合数为1。 下标⾮0的dp[j]初始化为0,这样累计加dp[j - coins[i]]的时候才不会影响真正的dp[j]。

4. 确定遍历顺序

个数可以不限使⽤,说明这是⼀个完全背包。得到的集合是排列,说明需要考虑元素之间的顺序。
本题要求的是排列,那么这个for循环嵌套的顺序可以有说法了。
动态规划完全背包问题02:零钱兑换 II - 掘金 (juejin.cn)中中就已经讲过了
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
如果把遍历nums(物品)放在外循环,遍历target的作为内循环的话,举⼀个例⼦:计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有{3,1}这样的集合,因为nums遍历放在外层,3只能出现在1后⾯!
所以本题遍历顺序最终遍历顺序:target(背包)放在外循环,将nums(物品)放在内循环,内循环从前到后遍历。 代码如下:

        for(int i = 1; i <= target; i++){//外层背包
            for(int j = 0; j < nums.length; j++){//内层物品
                if(i >= nums[j])
                    dp[i] += dp[i - nums[j]]; 
            }
        }

5. 举例推导dp数组

我们再来⽤示例中的例⼦推导⼀下:

image.png
分析完毕Java代码如下:

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target + 1];
        dp[0] = 1;
        for(int i = 1; i <= target; i++){
            for(int j = 0; j < nums.length; j++){
                if(i >= nums[j])
                    dp[i] += dp[i - nums[j]]; 
            }
        }
        return dp[target];
    }
}