动规规划-完全背包问题

news2024/12/23 3:41:26

有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。 首先回顾一下0-1背包问题,它和完全背包问题的区别就是是否可以多次取;那么我们再看下0-1背包的状态转移方程:dp[n][j] = max (dp[n-1][j] , dp[n-1][j-v[n]]+v[n]) ,其中,dp[n-1][j] = dp[n-1][j-0v[n]]+0v[n] 。我们很容易发现0-1背包的状态方程建立是考虑取不取的问题,那么完全背包问题就考虑取几个的问题,因此只需要改下0-1背包的状态方程就可以,将0、1改成k,k表示某个物品最多取k次。dp[n][j] = max (dp[n-1][j-countarr[n]]+countarr[n]) ,其中0<=count<=j/arr[n]

1、 本题为经典的完全背包问题,但是下列传统思路的代码会超时,需要进一步改进!实际上,复杂度的提升是决策层循环带来的,因此将决策层优化就能提升效率。

class Solution:
    def back_pack_i_i_i(self, a , v , m: int) -> int:
        # write your code here
        # 动规思路:dp[n][j] 表示前n个物品取不超过j的体积的最大价值
        # dp[n][j] = max (dp[n-1][j-count*arr[n]]+count*arr[n]) ,其中0<=count<=j/arr[n]

        dp = [[-1] * (m + 1) for _ in range(len(a))]

        for j in range(m+1):
            count = j // a[0]
            for c in range(count, -1, -1):  #决策层循环
                if c * a[0] <= j:
                    dp[0][j] = c * v[0]
                    break
                else:
                    dp[0][j] = 0

        def func(n, j):
            if n == 0:
                return dp[n][j]

            res = 0
            count = j // a[n]
            for c in range(count + 1):  #决策层循环
                if dp[n - 1][j - c * a[n]] == -1:
                    dp[n - 1][j - c * a[n]] = func(n - 1, j - c * a[n])
                res = max(res, dp[n - 1][j - c * a[n]] + c * v[n])
            return res

        return func(len(a) - 1, m)

 其实决策层那里经过数学推导可以得到:dp[n][j]=max(dp[n-1][j],dp[n][j-v[n]]+v[n]); 这与0-1背包非常相似,我们看下0-1背包的方程:dp[n][j] = max (dp[n-1][j] , dp[n-1][j-v[n]]+v[n])。其实区别就是在后面取最后一个物品的时候算前n个而不是n-1个。01背包考虑是放或者不放,所以考虑前n-1个物品的最大值与最后一个物品取不取的关系,而完全背包是不放或者放多件的关系,考虑的是前n件物品的最大值与还能不能放的问题。反正记住一点就行,完全背包的max中第二项考虑前n个,其他与0-1背包一样,代码如下:

class Solution:
    import sys  # 导入sys模块
    sys.setrecursionlimit(100000)  # 将默认的递归深度修改为100000
    def back_pack_i_i_i(self, a , v , m: int) -> int:
        # write your code here
        # 动规思路:dp[n][j] 表示前n个物品取不超过j的体积的最大价值
        # dp[n][j] = max (dp[n-1][j] , dp[n][j-v[n]]+v[n])
        if(len(a)==0):
            return 0

        dp = [[-1] * (m + 1) for _ in range(len(a))]

        for j in range(m+1):
            count = j // a[0]
            for c in range(count, -1, -1):  #优化决策层循环
                if c * a[0] <= j:
                    dp[0][j] = c * v[0]
                    break
                else:
                    dp[0][j] = 0

        def func(n, j): 
            if n == 0:
                return dp[n][j]
            if j==0:
                return 0

            if dp[n-1][j]==-1:
                dp[n - 1][j] = func(n-1,j)
            res = dp[n - 1][j]

            if j>=a[n]:
                if dp[n][j-a[n]]==-1:
                    dp[n][j - a[n]] = func(n,j-a[n])
                res = max(res,dp[n][j - a[n]]+v[n])
            dp[n][j] = res
            return res

        return func(len(a) - 1, m)

该题是经典的求方案数,我们知道动态规划善于用来解决 求最值、求方案数、求可行性三种题目,那么本题就是求方案数。但是本题要看清楚是只能取一次还是能够重复取,如果是重复取的话就是完全背包问题。先用0-1背包构建状态方程,然后再改n-1为n。请参考下面代码:

class Solution:
    def change(self, amount: int, coins ) -> int:

        # 动规思路:dp[n][j]表示前n个数凑成j的方案数
        # dp[n][j] = max(dp[n-1][j],dp[n-1][j-a[n]]) 0-1背包问题
        # dp[n][j] = max(dp[n-1][j],dp[n][j-a[n]])   完全背包问题
        if amount==0:
            return 1

        dp = [[-1] * (amount + 1) for _ in range(len(coins) + 1)]

        # 老是初始化出错,记得完全背包初始化时要考虑多个
        for j in range(amount+1):
            if j >= coins[0] and j // coins[0] == j / coins[0]:
                dp[0][j] = 1
            else:
                dp[0][j] = 0

        def func(n, j):
            if n == 0:
                return dp[n][j]
            if j == 0:
                return 1

            if dp[n - 1][j] == -1:
                dp[n - 1][j] = func(n - 1, j)
            res = dp[n - 1][j]

            if j >= coins[n]:
                if dp[n][j - coins[n]] == -1:
                    dp[n][j - coins[n]] = func(n, j - coins[n])
                res = dp[n - 1][j] + dp[n][j - coins[n]] 
            dp[n][j] = res
            return res

        return func(len(coins) - 1, amount)
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #完全背包问题 dp[i][j] 前i个数凑成j所需的最少银币数
        #dp[i][j] = min(dp[i-1][j],dp[i][j-coins[i]])
        if amount==0:
            return 0

        dp = [[99999999]*(amount+1) for _ in range(len(coins)+1)]

        for j in range(amount+1):
            if j//coins[0]==j/coins[0]:
                dp[0][j] = j//coins[0]
            # else:
                # dp[0][j] = 0  # 注意 不能赋值为0 直接不用赋值就行

        for i in range(1,len(coins)):
            for j in range(0,amount+1):  # 注意遍历开始条件
                if j>=coins[i]:
                    dp[i][j] = min(dp[i-1][j],dp[i][j-coins[i]]+1)
                else:
                    dp[i][j] = dp[i-1][j]

        res = dp[len(coins)-1][amount]
        return -1 if res==99999999 else res

注意此题和上一题的区别,上一题是完全背包的典型例题,不考虑排列方式,本题比上一题难在排列方式,如果不考虑排列代码如上面零钱兑换那题:凡是考虑装满背包的方案数的问题都是考虑 dp[i] += dp[i-num[j]] 的所有方案,dp[i] 表示总和为i的所有排列数。

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        #动规思路:前缀完全背包动态问题 dp[i][j]表示前i个数为j的组合个数
        #方程建立: dp[i][j] = max(dp[i-1][j],dp[i][j-nums[i]])
        n = len(nums)
        dp = [[-1]*(target+1)  for _ in range(n+1)]

        #初始化 
        for j in range(target+1):
            if nums[0]==j:
                dp[0][j] = 1
            else:
                dp[0][j] = 0

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

        #递归方程
        def func(i,j):
            if i==0 or j==0:
                return dp[i][j]

            if dp[i-1][j]==-1:
                dp[i-1][j]=func(i-1,j)
            res = dp[i-1][j]

            if j>=nums[i]:
                if dp[i][j-nums[i]]==-1:
                    dp[i][j-nums[i]]=func(i,j-nums[i])
                # res = dp[i-1][j] + dp[i][j-nums[i]]  #这样写就是不考虑排列方式
                res = dp[i-1][j] + 复杂的排列组合公式
            dp[i][j] = res 
            return res 

        res = func(n-1,target)
        return res

考虑组合方式的代码如下:

class Solution:
    def numSquares(self, n: int) -> int:
        #转化为完全背包问题,物品就是完全平方数

        nums = []
        for i in range(1,n+1):
            if(i*i<=n):
                nums.append(i*i)
            else:
                break
        m = len(nums)
        dp = [[999999]*(n+1) for _ in range(m+1) ]
        for j in range(n+1):
            if (j//nums[0]==j/nums[0]):
                dp[0][j] = j//nums[0]

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

        return dp[m-1][n]

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/340166.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【寒假day4】leetcode刷题

&#x1f308;一、选择题❤1.下列哪一个是析构函数的特征&#xff08; &#xff09;。A: 析构函数定义只能在类体内 B: 一个类中只能定义一个析构函数 C: 析构函数名与类名相同 D: 析构函数可以有一个或多个参数答案&#xff1a;B答案解析&#xff1a;析构函数是构造函…

练 习

1.判断三个中最重的//依次输入相应的人的体重double people1, people2, people3;cout << "请输入第一个人体重" << endl;cin >> people1;cout << "请输入第二个人体重" << endl;cin >> people2;cout << "请…

开发人员与测试人员关系的理解

在软件开发中都会有开发人员&#xff08;以下简称开发&#xff09;和测试人员&#xff08;以下简称测试&#xff09;&#xff0c;在一些小型公司可能并没有测试&#xff0c;仅仅是开发兼任测试。在这里我仅针对于有专业的测试和专业的开发的项目。 每个公司应该都有考核机制&am…

solidwork中添加GB型材库以及使用

在使用solidwork设计遮光罩的时候&#xff0c;需要铝型材作为支架&#xff0c;铝型材如果自己去画设计可能浪费时间不说&#xff0c;还得去找对应型号。默认solidwork中没有国标的型材库。网上教程导入型材库路径是错误的&#xff0c;所以在复制完后打开solidwork找不见型材库 …

Ubuntu小操作

安装拼音输入法 sudo apt-get install ibus-pinyin点击加号 点击other 输入 chinese 使用 winspace 切换 可以在键盘快捷键处改变快捷键 安装ADB sudo apt-get install android-tools-adb编写规则 sudo gedit /etc/udev/rules.d/80-android.rules写入(其中红色字体部…

面试实战篇 | 快手本地生活,结合项目谈Redis实战项目场景?MySQL InnoDB存储引擎如何工作的?策略模式?

本期是【你好&#xff0c;面试官】系列文章的第21期&#xff0c;持续更新中…。 《你好&#xff0c;面试官》系列目前已经连载20篇了&#xff0c;据说看了这个系列的朋友都拿到了大厂offer~ 你好&#xff0c;面试官 | 你真的理解面向 “对象”&#xff1f;你好&#xff0c;面…

在Flask中构建API接口的相关概念

重定向行为斜杠以下两个路由的不同之处在于是否使用尾部的斜杠。第一个路由的URL尾部有一个斜杠&#xff0c;看起来就像一个文件夹&#xff0c;访问一个没有斜杠结尾的URL时&#xff0c;Flask会自动进行重定向&#xff0c;在结尾加上一个斜杠。第二个路由的URL没有尾部斜杠&…

ChatGpt-2 对话单页功能实现vue3+elementplus

接口可参照CHATGPT.NET自行实现 <script setup> import { RouterLink, RouterView } from "vue-router"; import HelloWorld from "./components/HelloWorld.vue"; import { MoreFilled ,CircleCheck} from "element-plus/icons-vue"; …

GIS数据格式坐标转换(地球坐标WGS84、GCJ-02、火星坐标、百度坐标BD-09、国家大地坐标系CGCS2000)

文章目录前言一、坐标系1.地球坐标 (WGS84)2.国测局坐标系(GCJ-02、火星坐标系)3.百度坐标(BD-09)4.国家大地2000坐标系(CGCS2000)二、百度坐标系(BD-09) 与火星坐标系(GCJ-02)的转换1.核心代码2.转换验证百度地图高德地图腾讯地图三、火星坐标系 (GCJ-02) 与百度坐标系 (BD-09…

【青训营】RPC框架分层设计简述

远程函数调用&#xff0c;Remote Procedute Calls&#xff0c;简称RPC。RPC需要解决的问题有&#xff1a;完成远程函数的映射&#xff0c;并且将数据转化为字节流&#xff0c;使用网络进行传输 RPC基本概念 IDL文件&#xff1a;IDL通过一种中立的方式来描述接口&#xff0c;使…

OpenCV 图像梯度算子

本文是OpenCV图像视觉入门之路的第12篇文章&#xff0c;本文详细的介绍了图像梯度算子的各种操作&#xff0c;例如&#xff1a;Sobel算子Scharr算子laplacian算子等操作。 OpenCV 图像梯度算子目录 1 Sobel算子 2 Scharr算子 3 laplacian算子 1 Sobel算子 Sobel算子是一种图…

.Net Core对于`RabbitMQ`封装分布式事件总线

首先我们需要了解到分布式事件总线是什么&#xff1b;分布式事件总线是一种在分布式系统中提供事件通知、订阅和发布机制的技术。它允许多个组件或微服务之间的协作和通信&#xff0c;而无需直接耦合或了解彼此的实现细节。通过事件总线&#xff0c;组件或微服务可以通过发布或…

基于STM32的虚拟示波器

仓库地址 https://github.com/shuai132/ScopeMCU ScopeMCU Oscilloscope for MCU MCU: STM32F103C8Tx 需配合ScopeGUI使用 截图说明见wiki 最新版Releases Introduction 用最少的硬件成本&#xff0c;做一个实用的虚拟示波器。 这是硬件部分&#xff0c;基于STM32最小…

提取DataFrame中每一行的DataFrame.itertuples()方法

【小白从小学Python、C、Java】【计算机等级考试500强双证书】【Python-数据分析】提取DataFrame中的每一行DataFrame.itertuples()选择题关于以下python代码说法错误的一项是?import pandas as pddf pd.DataFrame({A:[a1,a2],B:[b1,b2]},index[i1,i2])print("【显示】d…

基于SpringBoot实现ChatGPT-QQ机器人

概述 近期ChatGPT火爆全球&#xff0c;在其官方网站上也列举了非常全面的应用案例&#xff0c;仅仅上线两个月活跃用户数已经达到1亿&#xff0c;成为历史上用户数增长最快的面向消费者的应用 快速体验 OpenAI官网对外提供了标准的 API 接口&#xff0c;可以通过HTTP请求进行…

SignalR注册成Windows后台服务,并实现web前端断线重连

注意下文里面的 SignalR 不是 Core 版本&#xff0c;而是 Framework 下的 本文使用的方式是把 SignalR 写在控制台项目里&#xff0c;再用 Topshelf 注册成 Windows 服务 这样做有两点好处 传统 Window 服务项目调试时需要“附加到进程”&#xff0c;开发体验比较差&#xf…

RibbitMQ 入门到应用 ( 二 ) 安装

3.安装基本操作 3.1.下载安装 3.1.1.官网 下载地址 https://rabbitmq.com/download.html 与Erlang语言对应版本 https://rabbitmq.com/which-erlang.html 3.1.2.安装 Erlang 在确定了RabbitMQ版本号后&#xff0c;先下载安装Erlang环境 Erlang下载链接 https://packa…

vue通用后台管理系统

用到的js库 遇到的问题 vuex和 localStorage区别 vuex在内存中&#xff0c;localStorage存在本地localStorage只能存储字符串类型数据&#xff0c;存储对象需要JSON.stringify() 和 parse()…读取内存比读取硬盘速度要快刷新页面vuex数据丢失&#xff0c;localStorage不会vuex…

图的基本介绍和表示方式

图的基本介绍 为什么要有图这个基本数据结构? 我们还学习过线性表&#xff08;数组、队列、链表和栈&#xff09;和树&#xff0c;但是我们可以发现&#xff0c;线性表局限于一个直接前驱&#xff08;就是只能有唯一一个前面的结点&#xff09;和一个直接后继的&#xff08;…

Python+dilb 简单实现人脸检测(附代码)

如果dilb和face_recognition第三方包安装失败&#xff0c;请移步到Python 解决dilb和face_recognition第三方包安装失败_水w的博客-CSDN博客 目录 一、环境配置 二、前期知识 1 读取图片 2 灰度转换 解决报错cv2.error: OpenCV(4.7.0) D:\a\opencv-python\opencv-python\o…