当前位置: 首页 > news >正文

动态规划 之 排列与组合问题

文章目录

  • 518.零钱兑换 II
  • 377.组合总和IV

在动态规划算法问题中,存在两种十分相似的问题,一个就是 找零钱问题,另一个是组合总和问题

找零钱问题,其实是零钱问题的一个组合问题,只要零钱的种类和数目相同,就是一种方案;组合总和问题 是一个排列问题,1,2 和 2,1 其实是两种情况的问题

问题的关键点:

  • 组合总和计算的是排列,完全背包计算的是组合。比如 1,2 和 2,1 这两个排列,在本题是有区别的,是两种方案;但在完全背包中这两个排列没有区别,只算一种方案。
  • 从代码上看,计算排列(本题)需要外层循环枚举体积,内层循环枚举物品;计算组合(完全背包)需要外层循环枚举物品,内层循环枚举体积。

518.零钱兑换 II

518.零钱兑换 II

在这里插入图片描述

思路分析:要硬币的种类数目不同,对应的数量不同 才算的是 不同的方案数目,所以动态规划的话,外层循环是遍历硬币的种类,内存循环是amount

递归:因为是相同种类的物品的选择,数目相同的时候,是算同一种方案的,所以要使用两个变量,i用来记录当前遍历到的物品,c用于表示当前所剩余的金额(空间)

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        # 先使用记忆化搜索
        @cache
        # 前i种物品组成金额c的方案数目
        def dfs(i,c):
            # 如果遍历完,并且金额为0 就返回1种方案
            if i < 0:
                return 1 if c == 0 else 0
            # 不够的情况,就递归前i-1种
            if c < coins[i]:
                return dfs(i-1,c)
            return dfs(i-1,c) + dfs(i,c-coins[i])
        return dfs(len(coins) - 1,amount)

转换为动态规划:

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        n = len(coins)
        f = [[0] * (amount + 1) for _ in range(n + 1)]
        f[0][0] = 1
        for i, x in enumerate(coins):
            for c in range(amount + 1):
                if c < x:
                    f[i + 1][c] = f[i][c]
                else:
                    f[i + 1][c] = f[i][c] + f[i + 1][c - x]
        return f[n][amount]

当然,这个二维的动态规划,我们可以转化为一维

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        f = [1] + [0] * amount
        for x in coins:
            for c in range(x, amount + 1):
                f[c] += f[c - x]
        return f[amount]

377.组合总和IV

377.组合总和IV

在这里插入图片描述

思路分析:这个题目就是爬楼梯的变形,爬楼梯的原型是每次可以向上爬一个或者2步,问可以到达顶部n的方案的次数,而在这题的时候,每次向上的步数是nums[i]里面的数,顶部的高度是 target

和零钱问题的对比,由于我们每次的选择的步伐的大小都可以重复的,所以我们并不用传递两个参数,只用传递剩余的空间i,而在每一个空间i下的递归,我们都会逐一枚举可能得步伐

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        @cache
        def dfs(i):
        	# 到达底部返回
            if i == 0:
                return 1
            # return sum(dfs(i-x) for x in nums if x<= i)
            ans = []
            # 枚举可以移动的步数
            for x in nums:
                if x <= i:
                    ans.append(dfs(i-x))
            return sum(ans)
        return dfs(target)

1:1转化为动态规划

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        # 排列的问题,物品可以重复,物品放内层循环
        # def dfs(i):
        # 	# 到达底部返回
        #     if i == 0:
        #         return 1
        #     ans = []
        #     # 枚举可以移动的步数
        #     for x in nums:
        #         if x <= i:
        #             ans.append(dfs(i-x))
        #     return sum(ans)
        # 转化为动态规划
        dp = [1]+[0]*target
        for i in range(1,target+1):
            ans = 0
            for x in nums:
                if x<= i:
                    ans+=dp[i-x]
            dp[i] = ans
        return dp[target] 

相关文章:

  • SpringBoot(接受参数相关注解)
  • Unity序列化多态数组
  • PyQt6/PySide6 的 QThread 类
  • 18.Python实战:实现年会抽奖系统
  • 计算机网络原理试题二
  • 1317:【例5.2】组合的输出
  • Spring Boot中如何自定义Starter
  • DC-6靶机渗透测试全过程
  • matlab平面波展开法计算的二维声子晶体带隙
  • 代码讲解系列-CV(三)——Transformer系列
  • SQL 建表语句详解
  • (前端基础)HTML(二)
  • 用xml配置spring, bean标签有哪些属性?
  • 机器学习实战(3):线性回归——预测连续变量
  • 小小小病毒(3)(~_~|)
  • 51单片机-数码管
  • Java函数计算冷启动从8s到800ms的优化实录
  • Go 切片导致 rand.Shuffle 产生重复数据的原因与解决方案
  • 2024 年 9 月青少年软编等考 C 语言三级真题解析
  • 多个用户如何共用一根网线传输数据
  • 迪拜金融市场CEO:2024年市场表现出色,超八成新投资者来自海外
  • 上海明后天将迎强风大雨,陆地最大阵风7~9级
  • 央行行长详解降息:将通过利率自律机制引导商业银行相应下调存款利率
  • 新质观察|“模速空间”如何成为“模范空间”
  • 特朗普关税风暴中的“稳”与“变”:新加坡国会选举观察
  • 竞彩湃|新科冠军利物浦留力?纽卡斯尔全力冲击欧冠