完全背包问题

完全背包和0-1背包的区别在于每个物品可以选择多次,也是通过画表可以方便得到转移方程。
其实状态的转移无论是target在外循环还是内循环,都不会出现3 = 【1,2】,【2,1】的情况。

0/1背包
定义:物品只能用一次

dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])

完全背包
定义:物品可以用无限次
dp[i][j] = max(dp[i-1][j], dp[i][j-weight[i]] + value[i])

比较的是第i行前面的dp[i][j-weight[i]],因为前面的已经都是最佳的多次选择的case,针对某一个物品,可以多次选择,此时前面的j已经安排好了,所以此时必须使用的是第i行前面的j,而不是0-1背包的i-1行,因为满足weight是j的最佳的配置已经更新到了第i行。

由二维转一维也是同样的道理。

0/1背包
定义:物品只能用一次
for i in range(n):
for j in range(amount, -1, -1):
dp[j] = max(dp[j], dp[j-weight[i]] + value[i])
因为此时第i行必须使用的是第i-1行的结果,所以倒序的话就不会使得后面要使用的提前改变了。

完全背包
定义:物品可以用无限次
for i in range(n):
for j in range(amount+1):
dp[j] = max(dp[j], dp[j-weight[i]] + value[i])
因为此时需要使用的第i行的结果,所以直接使用顺序进行。与上述对比。

322. 零钱兑换—最少硬币数

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

你可以认为每种硬币的数量是无限的。
示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:

输入:coins = [2], amount = 3
输出:-1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # 完全背包问题
        n = len(coins)

        if amount <= 0:
            return 0

        nums = coins
        dp = [amount+1]*(amount+1)

        for i in range(n):
            dp[0] = 0

        for i in range(0, n):
            for j in range(1, amount+1):
                if j - nums[i] >= 0:
                    dp[j] = min(dp[j-nums[i]] + 1, dp[j])
                
        if dp[-1] == amount + 1:
            return -1
        else:
            return dp[-1]

对于amount类的,还是设置长度为amount+1,0处设置为初始状态~~

如果是用二维的数组转移公式来表示则为:dp[i][j]表示使用前i枚硬币组成价值为j,至少需要多少枚硬币。
dp[i][j] = min(dp[i][j-nums[i]]+1, dp[i-1][j]), j - nums[i] >= 0
dp[i][j] = dp[i-1][j] , j-nums[i] < 0
一维数组的转移:
dp[j] = min(dp[j], dp[j-nums[i]]+1), j-nums[i] >= 0----注意可以重复使用硬币,i在外循环,j在内循环。
dp[j] = dp[j], j-nums[i] < 0

零钱兑换—最多组合数

组合的题目还分组合能否重合[1,1,2]和[2,1,1]是否相同,正常是相同的,除非特殊交代~~
dp[i][j] = dp[i-1][j] + dp[i][j-nums[i]] ----i-1枚硬币凑成j的组合数加上i枚硬币凑成j-nums[i]的组合数,就可以得到总的。
dp[i][j] = dp[i-1][j], j-nums[i] < 0
特别注意一下初始条件,全部不选择也是一个组合,dp[i][0] = 1

注意这里并没有组合数的+1,特别是dp[i][j-nums[i]],并没有增加组合数···

因为这是个组合问题,我们不关心硬币使用的顺序,而是硬币有没有被用到。是否使用第k个硬币受到之前情况的影响。可以对比下面的爬楼梯,爬楼梯是排列问题~~~在这里插入图片描述

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        if not amount:  ###注意,amount为0的情况下是1,全部选择数为0也是一个选择
            return 1
        if not coins:
            return 0
        dp = []
        for i in range(1+len(coins)):
            dp.append([0]*(amount+1)) # 只要满足了减到0,那么就是1了,在1的基础上,dp[1] = dp[0] 
        for i in range(1+len(coins)):
            dp[i][0] = 1   

        for i in range(1, len(coins)+1):
            for j in range(1, amount+1): # 二维两者可以颠倒,但是一维不行!!
                if j - coins[i-1] >= 0:
                    dp[i][j] = dp[i][j-coins[i-1]] + dp[i-1][j]
                else:
                    dp[i][j] = dp[i-1][j]
        return dp[-1][-1]

在这里插入图片描述
每次可以爬多少阶,也就是硬币的类型~~但是这里是排列,可以重复。

class Solution:
    def climbStairs(self, n: int) -> int:
        dp = [0]*(n+1)
        dp[0] = 1
        for i in range(1, n+1):
            if i - 2 >= 0:
                dp[i] = dp[i-1] + dp[i-2]
            else:
                dp[i] = dp[i-1]
        return dp[-1]

扩展成每次可以爬steps的阶梯:

class Solution:
    def climbStairs(self, n: int) -> int:
      
        dp = [0]*(n+1)
        dp[0] = 1
        for i in range(1, n+1):
            if i - 2 >= 0:
                dp[i] = dp[i-1] + dp[i-2]
            else:
                dp[i] = dp[i-1]
        return dp[-1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值