DP初入门

news2025/1/10 16:19:28

目录

一、前言

二、DP概念

1、最少硬币问题

2、DP的两个特征

三、0/1背包(最经典的DP问题)

1、小明的背包1(lanqiaoOJ题号1174)

2、空间优化:滚动数组

1)交替滚动

2)自我滚动


一、前言

本文讲解了DP的基础概念和一道DP(01背包)例题,介绍了滚动数组的优化。

二、DP概念

1、最少硬币问题

【回顾贪心解法】

硬币面值1、2、5。支付13元,要求硬币数量最少。

贪心:1)5元硬币,2个

           2)2元硬币,1个

           3)1元硬币,1个

硬币面值 1、2、4、5、6。支付9元。

贪心:1)6元硬币,1个

           2)2元硬币,1个

           3)1元硬币,1个。

错误!

答案是:5元硬币+4元硬币=2个

硬币问题的正解是动态规划。

type = [1, 5, 10, 25, 50]                        #5种面值

定义数组 Min[] 记录最少硬币数量:

对输入的某个金额 i,Min[i] 是最少的硬币数量。

第一步:只考虑1元面值的硬币金额

i=1元时,等价于:

(i=i-1=0) 元需要的硬币数量+1个1元硬币

把 Min[] 叫做 “状态”

把 Min[] 的变化叫做 “状态转移”

继续,所有金额仍然都只用1元硬币

i=2元时,等价于:(i=i-1=1)元需要的硬币数量,加上1个1元硬币。

i=3元时…..

i=4元时..... 

在1元硬币的计算结果基础上,再考虑加上5元硬币的情况。从i=5开始就行了:

i=5元时,等价于:

1) i=i-5=0 元需要的硬币数量,加上1个5元硬币。Min[5]=1。

2)原来的 Min[5]=5。

取 (1)、(2) 的最小值,所以 Min[5]=1。

i=6时,等价于:

1)i=i-5=1 元需要的硬币数量,加上1个5元硬币。Min[6]=2

2)原来的 Min[6]=6

取 (1) (2) 的最小值,所以 Min[6]=2

i=7时,…

i=8元时,…

用1元和5元硬币,结果:

递推关系:(状态转移方程)

Min[i] = min(Min[i], Min[i-5]+1)

继续处理其它面值硬币。

def solve(s):
    Min=[int(1e12)]*(s+1)   #初始化为无穷大
    Min[0]=0
    for j in range(cnt):    #5种硬币
        for i in range(money[j],s+1):
            Min[i]=min(Min[i],Min[i-money[j]]+1)
    print(Min[s])

cnt=5                   #5种面值
money=[1,5,10,25,50]    #面值可换
s=int(input())
solve(s)

注意:我们习惯把状态命名为 dp[],即上面代码的 Min 改为 dp。

2、DP的两个特征

1)重叠子问题。子问题是原大问题的小版本,计算步骤完全一样;计算大问题的时候,需要多次重复计算小问题。

一个子问题的多次计算,耗费了大量时间。用DP处理重叠子问题,每个子问题只需要计算一次,从而避免了重复计算,这就是DP效率高的原因。

2)最优子结构。首先,大问题的最优解包含小问题的最优解;其次,可以通过小问题的最优解推导出大问题的最优解。

补充:

1)记忆化

  • 如果各个子问题不是独立的,如果能够保存已经解决的子问题的答案,在需要的时候再找出已求得的答案,可以避免大量的重复计算。
  • 基本思路:用一个表记录所有已解决的子问题的答案,不管该问题以后是否被用到,只要它被计算过,就将其结果填入表中。
  • 这就是记忆化。

2)求解过程

三、0/1背包(最经典的DP问题)

  • 给定 n 种物品和一个背包,物品 i 的重量是 wi,其价值为 vi,背包的容量为 C。
  • 背包问题:选择装入背包的物品,使得装入背包中物品的总价值最大
  • 如果在选择装入背包的物品时,对每种物品 i 只有两种选择:装入背包或不装入背包,称为0/1背包问题。

  • 设 xi 表示物品 i 装入背包的情况:

        xi=0,表示物品 i 没有被装入背包

        xi=1,表示物品 i 被装入背包

  • 约束条件:

  • 目标函数:

【举例子】

例:有 5 个物品,重量分别是 {2, 2, 6, 5, 4},价值分别为 {6, 3, 5, 4, 6},背包的容量为10。

定义一个 (n+1)×(C+1) 的二维表 dp[][]

表示把前 i 个物品装入容量为 j 的背包中获得的最大价值。

填表:按只放第 1 个物品、只放前 2 个、只放前 3 个 …...一直到放完,这样的顺序考虑。(从小问题扩展到大问题)

1、只装第1个物品。(横向是递增的背包容量)

2、只装前2个物品。

如果第 2 个物品重量比背包容量大,那么不能装第2个物品,情况和只装第 1 个一样。

如果第 2 个物品重量小于背包容量,那么:

1)如果把物品 2 装进去 (重量是2),那么相当于只把 1 装到 (容量-2) 的背包中。

2)如果不装 2, 那么相当于只把 1 装到背包中。

一一取(1)和(2)的最大值。

接着继续更新表格。

1)如果把物品 2 装进去 (重量是2),那么相当于只把 1 装到 (容量-2) 的背包中。

 2)如果不装 2, 那么相当于只把 1 装到背包中。

一一取(1)和(2)的最大值。

3、只装前3个物品。

如果第 3 个物品重量比背包大,那么不能装第 3 个物品,情况和只装第1、2个一样。

如果第 3 个物品重量小于背包容量,那么:

1)如果把物品 3 装进去(重量是6),那么相当于只把 1、2 装到 (容量-6) 的背包中。

2)如果不装 3,那么相当于只把 1、2 装到背包中。——取(1)和(2)的最大值。

按这样的规律一行行填表,直到结束。现在回头考虑,装了哪些物品。

看最后一列,15>14,说明装了物品 5,否则价值不会变化。 

DP复杂度?可以先自己想一想。

1、小明的背包1(lanqiaoOJ题号1174)

【题目描述】

小明有一个容量为 C 的背包。这天他去商场购物,商场一共有 N 件物品,第 i 件物品的体积为 ci,价值为 wi。小明想知道在购买的物品总体积不超过 C 的情况下所能获得的最大价值为多少,请你帮他算算。

【输入描述】

输入第 1 行包含两个正整数 N, C,表示商场物品的数量和小明的背包容量。第 2~N+1 行包含 2 个正整数 c, w,表示物品的体积和价值。1<=N<=10^2,1<=C<=10^3,1<=wi,ci<=10^3。

【输出描述】

输出一行整数表示小明所能获得的最大价值。

  • DP状态:定义二维数组dp[][],大小为 N×C。
  • dp[i][j]:把前 i 个物品 (从第1个到第i个) 装入容量为 j 的背包中获得的最大价值。
  • 把每个 dp[i][j] 看成一个背包:背包容量为 j,装 1~i 这些物品。最后得到的 dp[N][C] 就是问题的答案:把 N个物品装进容量 C 的背包的最大价值。

【下面的分析是精髓⭐⭐⭐⭐⭐】

递推计算到 dp[i][j],分 2 种情况:

1)第 i 个物品的体积比容量 j 还大,不能装进容量 j 的背包。那么直接继承前 i-1 个物品装进容量 j 的背包的情况即可:dp[i][j] = dp[i-1][j]。

2)第 i 个物品的体积比容量 j 小,能装进背包。又可以分为 2 种情况:装或者不装第 i 个。

①装第 i 个。从前 i-1 个物品的情况下推广而来,前 i-1 个物品是 dp[i-1][j]。第 i 个物品装进背包后,背包容量减少 c[i],价值增加 w[i]。有:dp[i][j] = dp[i-1][j-c[i]] + w[i]。

②不装第 i 个。那么:dp[i][j] = dp[i-1][j]。

取①和②的最大值,状态转移方程:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-c[i]] +w[i])

def solve(n,C):
    for i in range(1,n+1):
        for j in range(0,C+1):
            if c[i]>j:
                dp[i][j]=dp[i-1][j]
            else:
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i])
    return dp[n][C]

N=3011
dp=[[0]*N for i in range(N)]
w=[0]*N
c=[0]*N
n,C=map(int,input().split())
for i in range(1,n+1):
    c[i],w[i]=map(int,input().split())
print(solve(n,C))

2、空间优化:滚动数组

把 dp[][] 优化成一维的 dp[],以节省空间。

dp[i][] 是从上面一行 dp[i-1] 算出来的,第 i 行只跟第 i-1 行有关系,跟更前面的行没有关系:

dp[i][i] = max(dp[i - 1][i], dp[i -1][i - c[i]] + w[i])

优化:只需要两行 dp[0][]、dp[1][],用新的一行覆盖原来的一行,交替滚动。

经过优化,空间复杂度从 O(N×C) 减少为 O(C)。

1)交替滚动

定义dp[2][i]:用 dp[0][] 和 dp[1][] 交替滚动。

优点:逻辑清晰、编码不易出错,建议初学者采用这个方法。

now 始终指向正在计算的最新的一行,old 指向已计算过的旧的一行。

对照原递推代码, now 相当于 i,old 相当于 i-1

def solve(n,C):
    old=1
    now=0
    for i in range(1,n+1):
        old,now=now,old
        for j in range(0,C+1):
            if c[i]>j:
                dp[now][j]=dp[old][j]
            else:
                dp[now][j]=max(dp[old][j],dp[old][j-c[i]]+w[i])
    return dp[now][C]

N=3011
dp=[[0]*N for i in range(2)]
w=[0]*N
c=[0]*N
n,C=map(int,input().split())
for i in range(1,n+1):
    c[i],w[i]=map(int,input().split())
print(solve(n,C))

2)自我滚动

继续精简:用一个一维的 dp[] 就够了,自己滚动自己。

def solve(n,C):
    for i in range(1,n+1):
        for j in range(C,c[i]-1,-1):
            dp[j]=max(dp[j],dp[j-c[i]]+w[i])
    return dp[C]

N=3011
dp=[0]*N
w=[0]*N
c=[0]*N
n,C=map(int,input().split())
for i in range(1,n+1):
    c[i],w[i]=map(int,input().split())
print(solve(n,C))

 注意:j 从小往大循环是错误的

例如 i = 2 时,左图的 dp[5] 经计算得到 dp[5] = 9,把 dp[5] 更新为 9。

右图中继续往后计算,当计算 dp[8] 时,得 dp[8] = dp[5]' + 3 = 9+3 = 12,这个答案是错的。

错误的产生是滚动数组重复使用同一个空间引起的。

而从大到小是对的。

以上,DP初入门

祝好

 

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

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

相关文章

C语言函数调用详解

所谓函数调用&#xff08;Function Call&#xff09;&#xff0c;就是使用已经定义好的函数。函数调用的一般形式为&#xff1a;functionName(param1, param2, param3 ...);functionName 是函数名称&#xff0c;param1, param2, param3 ...是实参列表。实参可以是常数、变量、表…

【Java开发】Spring Cloud 10 :Stream消息驱动

官方定义Spring Cloud Stream 是一个用来为微服务应用构建消息驱动能力的框架。它为一些供应商的消息中间件产品提供了个性化的自动化配置实现&#xff0c;Spirng Cloud Stream 本质上就是整合了 Spring Boot 和 Spring Integration&#xff0c;实现一套轻量级的消息驱动的微服…

Python内置包Tkinter的重要控件(下)

本文将接着介绍剩下的五个重要的控件&#xff0c;包括Canvas&#xff0c;Messagebox&#xff0c;Listbox&#xff0c;Checkbutton&#xff0c;Radiobutton。 目录 前言 控件 1. Canvas 2. Messagebox 3. Listbox 4. Radiobutton 5. Checkbutton 总结 前言 包括但不…

VBA提高篇_08 数据源类型判断 / 四舍五入

文章目录数据类型操作1. 数据类型判断2.数据类型转换2.1转换函数2.2 关于小数数据类型的四舍五入2.2.1 银行家舍入法2.2.2 Round()函数2.2.3 Int()函数数据类型操作 1. 数据类型判断 IsDate() 是否是日期类型 IsNumeric() 是否是数值类型 TypeName(x) 返回x 的数据类型的名称…

移动端特点和flex布局

移动端特点和flex布局移动端特点物理分辨率和逻辑分辨率视口视口标签二倍图百分比布局flex布局主轴对齐方式侧轴对齐方式伸缩比圣杯布局移动端特点 PC端/移动端不同 PC端 屏幕大&#xff0c;网页固定版心浏览器繁多&#xff0c;更多考虑兼容性问题。&#xff08;布局&#xf…

Gin+Vite实现单图上传

前言 参考文献&#xff1a;https://blog.csdn.net/heian_99/article/details/122447855 案例目的&#xff1a;实现前端上传图片并显示&#xff0c;后端保存图片&#xff1b; 技术&#xff1a;elementplus、axios、vue3、vite、gin 实现原理&#xff1a; 前端请求对应后端接口…

连通性1(Tarjan 理论版)

目录 一、无向图割点、桥、双连通分量 Tarjan 算法求割点和桥&#xff08;割边&#xff09; “割点”代码 边双和点双连通分量 边双连通分量 和 点双连通分量 的缩点 二、有向图强连通分量 1.有向图的弱连通与强连通 2.强连通分量 Kosaraju算法 Tarjan 算法&#xff08…

读书笔记:Python绘制三维图像 ← 斋藤康毅

下文给出了绘制函数 的 Python 代码。 很显然&#xff0c;这是一个三维图像。【绘制三维图像的Python代码】 import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3Dfigplt.figure() axAxes3D(fig) x1np.arange(-3.0, 3.0, 0.1) x2np.…

python刷题-关于日期、正则表达式的题

目录标题1、计算日期范围内的所有日期2、将Unix时间戳转换为格式化日期3、计算日期数据周同比4、正则表达式判断字符串是否是日期5、从文本中提取手机号码 --正则表达式6、批量提取网页上的手机号码7、自动提取电子邮箱地址8、验证用户密码是否规范-re.findall9、提取商品价格1…

ELK简介

什么是ELKE: Elasticsearch全文搜索引擎L: logstash日志采集工具K: kibana ES的可视化工具ELK是当今业界非常流行的日志采集保存和查询的系统我们编写的程序,会有很多日志信息,但是日志信息的保存和查询是一个问题IDEA控制台是一个临时显示的位置,我们可以将它保存在文件中但是…

Jetpack架构组件库:Room

Room Room是一款轻量级orm数据库&#xff0c;本质上是一个基于SQLite之上的抽象层。它通过注解的方式提供相关功能&#xff0c;编译时自动生成实现Impl&#xff0c;相比纯 SQLite 的API使用方式更加简单。另外一个相比于SQLite API的优势是&#xff1a;它会在编译时检查 SQL 语…

SpringBoot+Vue项目在线视频教育平台

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…

网络原理之HTTP/HTTPS、TCP、IP四层协议栈

文章目录一、应用层&#xff08;一&#xff09;xml协议&#xff08;二&#xff09;json协议&#xff08;三&#xff09;protobuffer协议&#xff08;四&#xff09;HTTP协议1. 抓包工具&#xff0c;fiddler2. HTTP报文格式3. HTTP请求(Request)&#xff08;1&#xff09;URL基本…

【VisualBasicApplication】Excel编程

VBAExcel的宏与VBA宏的录制宏的启动运行快捷键运行宏&#xff1a;使用Excel对象运行宏*VBA的数据类型字符串&#xff08;String&#xff09;整形&#xff08;Integer&#xff09;和长整形&#xff08;Long&#xff09;单精度浮点型&#xff08;Single&#xff09;和双精度浮点型…

3.mysql查询必备sql语句

文章目录1.条件查询 where2. 通配符与模糊查询3. 映射4. 排序 order_by5. 取部分 limit 和offset6. 分组 group by7.左右连表 left outer join ... on8. 联合查询 union1.条件查询 where 表内容&#xff1a; import pymysqlconn pymysql.connect(host127.0.0.1,port3306,u…

向QAbstractItemView子类如:QTreeView、QTableView等子项单元格插入窗体小部件的功能实现(第2种方法)

1.前言工作中经常会遇到这样的需求&#xff1a;向QAbstractItemView子类如QTreeView、QTableView单元格插入窗体小部件&#xff0c;如&#xff1a;进度条、按钮、单行编辑框等。下面链接的系列博文就是讲解如何实现该功能的。《向QAbstractItemView子类如:QTreeView、QTableVie…

LeetCode 2500. 删除每行中的最大值

给你一个 m x n 大小的矩阵 grid &#xff0c;由若干正整数组成。 执行下述操作&#xff0c;直到 grid 变为空矩阵&#xff1a; 从每一行删除值最大的元素。如果存在多个这样的值&#xff0c;删除其中任何一个。 将删除元素中的最大值与答案相加。 注意 每执行一次操作&#…

行为型模式-状态模式

1.概述 【例】通过按钮来控制一个电梯的状态&#xff0c;一个电梯有开门状态&#xff0c;关门状态&#xff0c;停止状态&#xff0c;运行状态。每一种状态改变&#xff0c;都有可能要根据其他状态来更新处理。例如&#xff0c;如果电梯门现在处于运行时状态&#xff0c;就不能…

时序数据处理中的拟合问题

对于深度学习或机器学习模型而言,我们不仅要求它对训练数据集有很好的拟合(训练误差),同时也希望它可以对未知数据集(测试集)有很好的拟合结果(泛化能力),所产生的测试误差被称为泛化误差。度量泛化能力的好坏,最直观的表现就是模型的过拟合(overfitting)和欠拟合(…

一起Talk Android吧(第四百七十五回:渐变类视图动画)

文章目录使用方法属性介绍示例代码共用属性各位看官们大家好&#xff0c;上一回中咱们说的例子是"如何使用视图动画",这一回中咱们说的例子是"渐变类视图动画"。闲话休提&#xff0c;言归正转&#xff0c;让我们一起Talk Android吧&#xff01; 看官们&am…