厦门大学暑期数学建模培训:动态规划(初阶)

文章目录


前言

这是一份来自学校暑期数学建模培训的内容,记录与生活实际相关的动态规划问题。

本文是基础动态规划与背包问题的延申,需要动态规划基础才能做

一、动态规划是什么?

动态规划求解需要跟随以下4步骤

1. 确定dp数组下角标含义(决定了能否写对递推公式)

2. 确定递推公式(最难部分需要理解并延申)

3. dp数组初始化(dp[0][0],dp[i][j]一开始的数值为无穷还是0)

4. 遍历方式(从前向后还是从后向前)

二、具体例子

1.设备分配问题

思路

背包问题统一将变量转换为熟悉的背包变量:工厂设备投入总量为背包容量;各工厂看作三种物品,一种物品放一次,但是物品的价值会随着放入的次数改变;利益就是背包的总价值。

也就是多重背包问题的变种。
但是数据量较少,这里不讨论二进制优化与队列优化,直接朴素算法

既然转换成了背包问题,就套用背包问题的公式

套动态规划公式

1. 下角标含义:背包问题中的dp[i][j]为总价值,i为物品,j为当前背包容量

2. 通项公式:其实可以将该问题看成套用三层循环的01背包问题

正常我们知道01背包问题其实只有两层循环:        ​               

        dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w_{i}] + v_{i})

        第一层:i物品循环,第二层:j容量循环

那么这第三层循环哪来的呢?关键就在于物品的价值会随着放入的次数改变。

此时物品总量还是i,但是w_{i}v_iw_{i}决定)可以由另外一个变量决定,所以决定w_{i}的另一个变量就可以作为第三层循环的变量——放入量k

那么此时的递推公式也就能推出来:

        dp[i][j] = \underset{k < =j}{max}(dp[i - 1][j], dp[i - 1][j - w_{ik}] + v_{ik})

        w_{ik}是放入k次i物品的重量,v_{ik}则是放入k次i物品的价值

3. 数组初始化:碰到max,而且答案不可能小于0时,默认全部取0

4. 遍历方式:默认参考背包问题,可以从前向后遍历

代码

total = [[3, 7, 9, 12, 13], [5, 10, 11, 11, 11], [4, 16, 11, 12, 12]]
dp = [[0] * 6 for _ in range(4)]  # 用于储存结果
memo = [[[]] * 6 for _ in range(4)]  # 用于储存路径

for i in range(1, 4):  # 第一层循环i种物品
    for j in range(6):  # 第二层循环背包容量j
        dp[i][j] = dp[i - 1][j]  # 初始化上一个状态
        memo[i][j] = memo[i - 1][j] + [0]  # 初始化上一个状态,未放入的情况
        for k in range(1, j + 1):  # 第三层循环放入i物品的次数
            this = total[i - 1][k - 1]  # 放入i次的价值
            value = dp[i - 1][j - k] + this  # j容量下放入k次i物品后的总价值
            if value > dp[i][j]:  # 更新最优解
                memo[i][j] = memo[i - 1][j - k] + [k]
                dp[i][j] = value

答案: 29
过程: [1, 2, 2]  # 甲一台设备,乙丙各两台设备

2.上一题的简单变式

思路

由于每个营业区至少增设一个,所以最简单的方法其实就是设置数据数组从销售店增加数1的位置开始,其余部分同第一题

背包问题统一将变量转换为熟悉的背包变量:销售店增加数为背包容量;各地区看作三种物品,一种物品放一次,但是物品的价值会随着放入的次数改变;利润就是背包的总价值。

代码(将第一题的ijk大小进行修改即可,可以自己尝试做一做)

total = [[200, 280, 330, 340], [210, 220, 225, 230], [160, 170, 180, 200]]
dp = [[0] * 4 for _ in range(4)]  # 由于每个地区至少一个商店,所以默认能增设的商店只有4个,也就是背包容量为4
memo = [[[]] * 4 for _ in range(4)]  # 计入路径

for i in range(1, 4):
    for j in range(4):
        for k in range(0, j + 1):  # 这里和上一题略有修改,更简洁
            this = total[i - 1][k]
            value = dp[i - 1][j - k] + this
            if value > dp[i][j]:
                dp[i][j] = value
                memo[i][j] = memo[i - 1][j - k] + [k]

3. 产品生产问题

思路

根据题意分析自变量:时期生产量。 因变量有:时期库存量总成本

对比背包问题自变量:  物品。因变量:背包容量总价值

一一对应起来发现:生产安排问题好像和背包问题很像!

所以转换为背包问题,时期作为物品k时期库存量作为背包容量总成本作为总价值

那么生产量这个自变量该怎么处理?答案就是三重循环

先了解一个公式:生产量u + 上一期库存量j - 需求量(常数d) = 本期库存量

        u_{i} + j\_ - d_{i} = j ,(公式变式得u_iu_{i} = j - j\_ + d_{i}

        u_{i} 代表了本期生产量,j\_是上一个时期的库存量,j是本期的库存量,d_i 是本期需求量

此时就将自变量转换为了3个自变量,可以用三重循环:i,j\_,j

套动态规划模板

1. 下角标含义:背包问题中的dp[i][j]为总成本,i为物品,j为当前背包容量

2. 通项公式:根据思路中的三变量,可以得出三个变量下的关系

            dp[i][j] = \underset{j\_}{min}(dp[i - 1][j], dp[i - 1][j\_] + u * VC + FC)

            VC是随着生产量变化的成本,FC是有生产下的固定成本,不生产时状态继承

3. 数组初始化:碰到最小化,统一采用float("inf")无穷初始化

4. 遍历方式:默认参考背包问题,可以从前向后遍历

代码

for i in range(1, 5):  # 第i个物品
    for j in range(7):  # 最多能存6个,再多就没有意义了,因为生产6个
        for j_ in range(7):  # j_是之前的存储量
            u = j + demand[i - 1] - j_  # 计算当期生产量
            if 0 <= u <= 6:  # 生产量合法
                cost = dp[i - 1][j_] + j * 0.5  # 计算当期剩余成本与过去的成本累加
                if u > 0:  # 当有生产的时候
                    cost += 3 + u  # 这里是正常计算生产成本
                if cost < dp[i][j]:
                    dp[i][j] = cost
                    memo[i][j] = memo[i - 1][j_] + [u]

答案: 20.5
过程: [5, 0, 6, 0]  # 最后结果为第一时期生产5件,第三时期生产6件


总结

主要讲述了01背包问题的变式,注意三重循环的使用

        可以通过自变量与因变量对比分析,将实际问题抽象为背包问题变量

        通过变量关系,确定第三个变量

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值