LeetCode算法题解(动态规划)|LeetCode518. 零钱兑换 II、LeetCode377. 组合总和 Ⅳ

一、LeetCode518. 零钱兑换 II

题目链接:518. 零钱兑换 II
题目描述:

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。 

题目数据保证结果符合 32 位带符号整数。

示例 1:

输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

示例 2:

输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3 。

示例 3:

输入:amount = 10, coins = [10] 
输出:1

提示:

  • 1 <= coins.length <= 300
  • 1 <= coins[i] <= 5000
  • coins 中的所有值 互不相同
  • 0 <= amount <= 5000
算法分析:

题目描述钱硬币数目不限,所以这道题是一道完全背包问题。

物品就是硬币的面额,而背包容量就是总金额。

按照动规五部曲来。

定义dp数组及下标含义:

dp[j]表示金额为j的总额所能凑成的硬币组合数。

递推公式:

dp[j]+=dp[j-coins[i],现有面额为coins[i]的硬币,那么总额为j的组合数可由总额为j-coins[i]的组合数推到而来。

初始化:

dp[0]初始化为1,乳沟初始化为0,后面所有情况都会变成0。

遍历顺序:

注意这里要求的是凑成总额的不同硬币的组合数,不强调元素之间的顺序。

例如:3=2+1,3=1+2是同一种情况。如果要求的是排列数那么就是两种情况。

所以这里要注意对物品和背包容量的遍历顺序。

如果要求的是组合数,那么先遍历物品在遍历容量(对于每种容量,无论每种物品取多少,但取每种物品的顺序是不会改变的),如果要求的是排列数,那么先遍历容量在遍历物品(对于每种容量,去物品的顺序肯能会改变)。

如果结果有误,可以打印dp数组来进行验证。

代码如下:

class Solution {
    public int change(int amount, int[] coins) {
        int[] dp = new int[amount + 1];
        dp[0] = 1;//初始化为1
        for(int i = 0; i < coins.length; i++) {//遍历不同面额的硬币
            for(int j = coins[i]; j <= amount; j++) {//遍历总金额
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }
}

二、LeetCode377. 组合总和 Ⅳ

题目链接:377. 组合总和 Ⅳ
题目描述:

给你一个由 不同 整数组成的数组 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

提示:

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

进阶:如果给定的数组中含有负数会发生什么?问题会产生何种变化?如果允许负数出现,需要向题目中添加哪些限制条件?

算法分析:

这道题和上道题一样都是完全背包问题,不同的是上道题求的是凑成总金额的组合数,而这道题是组成不目标整数的排列数(强调顺序可以改变)。

所以遍历的时候我们需要先遍历背包容量再遍历物品。

定义dp数组及下标含义:

dp[j]表示凑成目标整数j的不同序列数有dp[j]种。

递推公式:

dp[j]+=dp[j-nums[i]],现有数nums[i]那么凑成j的序列数dp[j]可由j-nums[i]的序列数推导而出。

初始化:

dp[0]=1,之后的每种情况都有它推导而来,所以置为1。

遍历顺序:

因为是求排列数,情调元素的顺序,所以先遍历背包容量(也即目标整数),在遍历物品(每个元素)。

可以打印dp数组进行验证。

代码如下:

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target + 1];
        dp[0] = 1;//初始化1
        for(int j = 0; j <= target; j++) {//遍历目标整数
            for(int i = 0; i < nums.length; i++) {//遍历每个元素
                if(j >= nums[i]) {
                    dp[j]+=dp[j-nums[i]];
                }
            }
        }
        return dp[target];
    }
}

总结

这两道题是完全背包问题的两种不同求法。

你可能感兴趣的:(Java算法题解,算法,leetcode,动态规划,java)