文章目录
前言
这是一份来自学校暑期数学建模培训的内容,记录与生活实际相关的动态规划问题。
本文是基础动态规划与背包问题的延申,需要动态规划基础才能做
一、动态规划是什么?
动态规划求解需要跟随以下4步骤
1. 确定dp数组下角标含义(决定了能否写对递推公式)
2. 确定递推公式(最难部分需要理解并延申)
3. dp数组初始化(dp[0][0],dp[i][j]一开始的数值为无穷还是0)
4. 遍历方式(从前向后还是从后向前)
二、具体例子
1.设备分配问题
思路
背包问题统一将变量转换为熟悉的背包变量:工厂设备投入总量为背包容量;各工厂看作三种物品,一种物品放一次,但是物品的价值会随着放入的次数改变;利益就是背包的总价值。
也就是多重背包问题的变种。
但是数据量较少,这里不讨论二进制优化与队列优化,直接朴素算法
既然转换成了背包问题,就套用背包问题的公式
套动态规划公式
1. 下角标含义:背包问题中的为总价值,i为物品,j为当前背包容量
2. 通项公式:其实可以将该问题看成套用三层循环的01背包问题
正常我们知道01背包问题其实只有两层循环:
第一层:i物品循环,第二层:j容量循环
那么这第三层循环哪来的呢?关键就在于物品的价值会随着放入的次数改变。
此时物品总量还是i,但是(由决定)可以由另外一个变量决定,所以决定的另一个变量就可以作为第三层循环的变量——放入量k
那么此时的递推公式也就能推出来:
是放入k次i物品的重量,则是放入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) = 本期库存量
,(公式变式得)
代表了本期生产量,是上一个时期的库存量,是本期的库存量, 是本期需求量
此时就将自变量转换为了3个自变量,可以用三重循环:
套动态规划模板
1. 下角标含义:背包问题中的为总成本,i为物品,j为当前背包容量
2. 通项公式:根据思路中的三变量,可以得出三个变量下的关系
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背包问题的变式,注意三重循环的使用
可以通过自变量与因变量对比分析,将实际问题抽象为背包问题变量
通过变量关系,确定第三个变量