数学建模笔记——动态规划

数学建模笔记------动态规划

  • 动态规划
    • [1. 模型原理](#1. 模型原理)
    • [2. 典型例题](#2. 典型例题)
      • [2.1 例1 凑硬币](#2.1 例1 凑硬币)
      • [2.2 例2 背包问题](#2.2 例2 背包问题)
    • [3. python代码实现](#3. python代码实现)
      • [3.1 例1](#3.1 例1)
      • [3.2 例2](#3.2 例2)

动态规划

1. 模型原理

动态规划是运筹学的一个分支,通常用来解决多阶段决策过程最优化问题。动态规划的基本想法就是将原问题转换为一系列相互联系的子问题,然后通过逐层地推来求得最后的解。目前,动态规划常常出现在各类计算机算法竞赛或者程序员笔试面试中,在数学建模中出现的相对较少,但这个算法的思想在生活中非常实用,会对我们解决实际问题的思维方式有一定启发。

动态规划组成部分:

  1. 确定状态:解动态规划的时候需要开一个数组,数组的每个元素需要明确代表什么,类似于确定数学题中X、Y的含义
  2. 转移方程:把状态表达成方程
  3. 初始条件和边界情况
  4. 计算顺序

2. 典型例题

2.1 例1 凑硬币

你有三种硬币,分别面值2元、5元和7元,每种硬币都有足够多,买一本书需要27元,如何用最少的硬币组合起来正好付清,不需要对方找钱

  1. 确定状态

    • 最后一步:

      虽然我们不知道最优策略是什么,但是最优策略肯定是有k校硬币, a 1 , a 2 , . . . . . . a k a_1,a_2,......a_k a1,a2,......ak加起来面值为27,所以一定存在有最后一枚硬币: a k a_k ak,除了这枚硬币,前面的面值加起来是 27 − a k 27-a_k 27−ak,两个关键点:

      • 我们不关心前面的 k − 1 k-1 k−1枚硬币是怎么拼出 27 − a k 27-a_k 27−ak的(可能有很多种拼法),而且我们现在甚至还不知道 a k a_k ak和 k k k是多少,但我们可以确定前面的硬币拼出了 27 − a k 27-a_k 27−ak
      • 因为是最优策略,所以拼出 27 − a k 27-a_k 27−ak的硬币数一定要最少,否则就不是最优策略
    • 子问题:

      • 最少可以用多少枚硬币拼出 27 − a k 27-a_k 27−ak
      • 原问题是最少可以用多少枚硬币可以拼出27
      • 我们将原问题可以转化为一个规模更小的子问题: 27 − a k 27-a_k 27−ak
    • 状态:

      我们可以设状态 f ( x ) = 最少用多少枚硬币拼出 x f(x)=最少用多少枚硬币拼出x f(x)=最少用多少枚硬币拼出x

  2. 转移方程

    对于任意 x x x
    f x = m i n { f x − 2 + 1 , f x − 5 + 1 , f x − 7 + 1 } fx=min\{fx-2+1,fx-5+1,fx-7+1\} fx=min{fx−2+1,fx−5+1,fx−7+1}

  3. 初始条件和边界情况

    • 转移方程有两个问题
      • x − 2 , x − 5 , 或 x − 7 x-2,x-5,或x-7 x−2,x−5,或x−7小于0怎么办
      • 什么时候停下来
    • 所以:
      • 如果不能拼出Y,那么就定义 f Y = fY= fY=正无穷,例如 f − 1 = f − 2 = f − 3 = ⋯ = f-1=f-2=f-3=\cdots= f−1=f−2=f−3=⋯=正无穷
      • 所以 f 1 = min ⁡ { f − 1 + 1 , f − 4 + 1 , f − 6 + 1 } = f1=\operatorname*{min}\{f-1+1,f-4+1,f-6+1\}= f1=min{f−1+1,f−4+1,f−6+1}=正无穷,表示拼不出来
      • 初始条件 f 0 = 0 f0=0 f0=0
  4. 计算顺序

    • 拼出 x x x所需要的最少硬币数: f x = m i n { f x − 2 + 1 , f x − 5 + 1 , f x − 7 + 1 } fx=min\{fx-2+1,fx-5+1,fx-7+1\} fx=min{fx−2+1,fx−5+1,fx−7+1}
    • 初始条件: f 0 = 0 f0=0 f0=0
    • 然后计算 f 1 , f 2 , . . . , f x f1,f2,...,fx f1,f2,...,fx

2.2 例2 背包问题

有一个小偷去偷东西,他的背包可以容纳总重量为 W W W的物品,现在有 n n n件物品,每件物品的重量为 w i w_i wi, 价值为 v i v_i vi ,求能够放进背包的物品的最大价值。

  1. 状态: d p i j dpij dpij表示前 i i i件物品放入容量为 j j j的背包中所获得的最大价值

  2. 状态转移方程:对于第 i i i件物品,可以选择放或不放

    • 如果不放,那么 d p i j = d p i − 1 j dpij=dpi-1j dpij=dpi−1j

    • 如果放,那么 d p i j = d p i − 1 j − w i + ν i dpij=dpi-1j-w_i+\nu_i dpij=dpi−1j−wi+νi

    • 选择获得最大价值的情况,即
      d p i j   =   m a x ( d p i − 1 j , d p i − 1 j − w i   +   v i ) dpij\:=\:max(dpi-1j,dpi-1j-w_i\:+\:v_i) dpij=max(dpi−1j,dpi−1j−wi+vi)

  3. 初始条件:

    • d p 0 0 = 0 dp 0 0 = 0 dp00=0,将前0个物品放入容量为0的背包中能获得的最大价值为0
    • 如果容量为 0,则无法放入任何物品, d p i 0 = 0 dpi0=0 dpi0=0
    • 如果没有物品可选,则无法放入任何物品, d p 0 j = 0. dp0j=0. dp0j=0.
  4. 求解顺序:从第一个物品开始,求解到 n n n

最终, d p n W dpnW dpnW即为问题的解

3. python代码实现

3.1 例1

python 复制代码
def coinChange(n):
    """用于计算找零的最少硬币数

    Args:
        n (int): 需要找零的金额
    return:
        int: 最少硬币数,如果无法找零则返回-1
    """
    dp = [float('inf')]*(n+1)  # 初始化动态规划数组
    dp[0] = 0  # 金额为0时,最少硬币数为0
    for i in range(1, n+1):
        for j in [2, 5, 7]:
            if i >= j:
                dp[i] = min(dp[i], dp[i-j]+1)
    if dp[n] == float('inf'):
        return -1
    else:
        return dp[n]


n = int(input("请输入需要找零的金额:"))
res = coinChange(n)
print("最少硬币数为:", res)

测试结果:

复制代码
请输入需要找零的金额:27
最少硬币数为: 5

3.2 例2

python 复制代码
def knapsack(weights, values, capacity):
    """用于求解0-1背包问题的动态规划算法

    Args:
        weights (int): 物品的重量列表
        values (int): 物品的价值列表
        capacity (int): 背包的容量
    return:
        int: 背包能装的最大价值
    """
    n = len(weights)  # 物品的数量
    dp = [[0 for j in range(capacity+1)] for i in range(n+1)]  # 初始化动态规划数组

    # 动态规划求解过程
    for i in range(1, n+1):
        for j in range(1, capacity+1):
            if j < weights[i-1]:
                dp[i][j] = dp[i-1][j]
            else:
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-weights[i-1]]+values[i-1])
    return dp[n][capacity]


w = input("请输入物品的重量列表(用逗号隔开):")
v = input("请输入物品的价值列表(用逗号隔开):")
weights = [int(i) for i in w.split(",")]
values = [int(i) for i in v.split(",")]
c = int(input("请输入背包的容量:"))
res = knapsack(weights, values, c)
print("背包能装的最大价值为:", res)

输入及输出:

复制代码
请输入物品的重量列表(用逗号隔开):1,2,3
请输入物品的价值列表(用逗号隔开):3,4,5
请输入背包的容量:5
背包能装的最大价值为: 9

请输入物品的重量列表(用逗号隔开):1,2,3
请输入物品的价值列表(用逗号隔开):4,5,6
请输入背包的容量:5
背包能装的最大价值为: 11
相关推荐
Omics Pro40 分钟前
3种蛋白结构输入方式!已申报欧洲发明专利
数据库·人工智能·python·机器学习·plotly
Psycho_MrZhang1 小时前
Codex 高效开发协作手册
python
HappyAcmen1 小时前
1.pdfplumber安装,PDF文字提取
python·pdf
弹简特1 小时前
【零基础学Python-收尾】10-Python第三方库的安装介绍
开发语言·python
itfallrain1 小时前
Spring 构造器循环依赖排查:@RequiredArgsConstructor + @Lazy 到底有没有生效
数据库·python·spring
嵌入式老牛1 小时前
液晶段码(米/日字格)识别—倾斜校正
opencv·算法·仿射变换
luj_17681 小时前
残熵算法:风险缓冲与效率优化的融合
c语言·开发语言·网络·经验分享·算法
小草cys2 小时前
NVIDIA 驱动(550版本)成功安装后安装支持 GPU 加速的 PyTorch
人工智能·pytorch·python
SilentSamsara2 小时前
Python 微服务全链路:gRPC + 链路追踪 + 服务网格接入
开发语言·分布式·python·微服务·架构
oddsand12 小时前
pgvector 三大相似度算法
人工智能·算法·机器学习