算法复习——动态规划篇之钢条切割问题

算法复习——动态规划篇之钢条切割问题

以下内容主要参考中国大学MOOC《算法设计与分析》,墙裂推荐希望入门算法的童鞋学习!

1. 问题背景

钢铁切割

​ 现有一段长度为10的钢条,可以零成本将其切割为多段长度更小钢条。

在这里插入图片描述

​ 比如有以下几种切割方案:

在这里插入图片描述

​ 问题是怎么合理切割,使总收益最大?

2. 问题定义

钢条切割问题(Rod Cutting Problem)

输入:

  • 钢条长度 n n n
  • 价格表 p [ l ] ( 1 ≤ l ≤ n ) p[l](1 \leq l \leq n) p[l](1ln):表示长度为 l l l的钢条价格

输出:

  • 求解一组切割方案 T = < c 1 , c 2 , … , c m > T=<c_1, c_2, \dots, c_m> T=<c1,c2,,cm>,令 m a x ∑ l = 1 m p c l max\sum_{l=1}^mp_{c_l} maxl=1mpcl

    s . t . ∑ l = 1 m c l = n s.t. \sum_{l=1}^mc_l=n s.t.l=1mcl=n

3. 问题简化

​ 比如有一根长度为10的钢条。

  • 假设至多切割1次
    • 枚举所有可能的切割位置
      • 不切: p [ 10 ] p[10] p[10]
      • 切割: p [ i ] + p [ 10 − i ] p[i]+p[10-i] p[i]+p[10i]
    • 最大收益 m a x 1 ≤ i ≤ 9 { p [ i ] + p [ 10 − i ] , p [ 10 ] } max_{1 \leq i \leq 9}\{p[i]+p[10-i], p[10]\} max1i9{p[i]+p[10i],p[10]}
  • 假设至多切割2次
    • 先将钢条切割一段
    • 在剩余钢条中继续切割
  • 原始问题不限制切割次数

​ 因此,可能存在最优子结构重叠子问题

4. 动态规划

4.1 问题结构分析

  • 给出问题表示:
    • C [ j ] C[j] C[j]:切割长度为 j j j的钢条可得最大总收益
  • 明确原始问题:
    • C [ n ] C[n] C[n]:切割长度为 n n n的钢条可得最大总收益

4.2 递推关系建立

4.2.1 分析最优(子)结构

在这里插入图片描述

C [ j ] = m a x 1 ≤ i ≤ j − 1 { p [ i ] + C [ j − i ] , p [ j ] } C[j]=max_{1 \leq i \leq j-1}\{p[i]+C[j-i], p[j]\} C[j]=max1ij1{p[i]+C[ji],p[j]}

4.2.2 构造递推公式

​ 对于每个钢条长度 j j j C [ j ] = m a x 1 ≤ i ≤ j − 1 { p [ i ] + C [ j − i ] , p [ j ] } C[j]=max_{1 \leq i \leq j-1}\{p[i]+C[j-i], p[j]\} C[j]=max1ij1{p[i]+C[ji],p[j]}

​ 这个问题和之前的动态规划问题最明显的区别在于它不再只依赖于常数个子问题,而是需要依赖于 j j j种值的枚举情况,在所有情况中找最大者。

4.3 自底向上计算

4.3.1 确定计算顺序
  • 初始化: C [ 0 ] = 0 C[0]=0 C[0]=0(切割长度为0的钢条,总收益为0)
  • 递推公式: C [ j ] = m a x 1 ≤ i ≤ j − 1 { p [ i ] + C [ j − i ] , p [ j ] } C[j]=max_{1 \leq i \leq j-1}\{p[i]+C[j-i], p[j]\} C[j]=max1ij1{p[i]+C[ji],p[j]}
4.3.2 依次求解问题

在这里插入图片描述

4.4 最优方案追踪

4.4.1 记录决策过程
  • 构造决策数组 r e c [ 1.. n ] rec[1..n] rec[1..n]

  • r e c [ j ] rec[j] rec[j]记录长度为 j j j钢条的最优切割方案,如果不切, r e c [ j ] = j rec[j]=j rec[j]=j;for则, r e c [ j ] = k rec[j]=k rec[j]=k

在这里插入图片描述

4.4.2 输出最优方案

​ 根据追踪数组,递归输出方案

5. 伪代码

RodCutting(p, n)

输入:钢条价格表 p [ 1.. n ] p[1..n] p[1..n],钢条长度 n n n

输出:最大收益 C [ n ] C[n] C[n],钢条切割方案

// 初始化
新建一维数组C[0..n],rec[0..n]
C[0] ← 0
// 动态规划
for j ← 1 to n do
	q ← p[j]
	rec[j] ← j
	for i ← 1 to j - 1 do
		if q < p[i] + C[j-i] then
			q ← p[i] + C[j-i]
			rec[j] ← i
		end
	end
	C[j] ← q
end
// 输出最优方案
while n > 0 do
	print rec[n]
	n ← n - rec[n]
end

​ 该算法的时间复杂度是 O ( n 2 ) O(n^2) O(n2)

  • 5
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TSP问题(Traveling Salesman Problem,旅行商问题)是一个经典的组合优化问题,它要求在给定的城市之间找到一条最短路径,使得每个城市只被经过一次,并且最终回到起点。 在本文中,我们将介绍如何使用Python解决TSP问题动态规划算法动态规划算法 动态规划算法是一种解决复杂问题的有效方法,它通常用于优化问题。TSP问题动态规划算法的思路是:将问题分解为子问题,然后通过计算子问题的最优解来逐步构建整个问题的最优解。 具体来说,我们可以使用以下步骤来解决TSP问题: 1. 定义状态:将TSP问题定义为一个二元组$(S,i)$,其中$S$表示已经经过的城市集合,$i$表示当前所在的城市。 2. 定义状态转移方程:我们定义$dp(S,i)$表示从城市$i$出发,经过集合$S$中所有城市的最短路径长度。状态转移方程为: $$ dp(S,i) = \begin{cases} 0 & \text{if } S=\{i\} \\ \min\limits_{j\in S,j\ne i}\{dp(S-\{i\},j)+dist[j][i]\} & \text{otherwise} \end{cases} $$ 其中$dist[i][j]$表示城市$i$到城市$j$之间的距离。 3. 初始状态:$dp(\{i\},i)=0$。 4. 最终状态:$dp(\{1,2,\cdots,n\},1)$即为所求的最短路径长度。 代码实现 下面是使用Python实现TSP问题动态规划算法的代码: ```python import math def tsp_dp(dist): n = len(dist) # 记录子问题的最优解 dp = [[math.inf] * n for _ in range(1 << n)] # 初始状态 for i in range(n): dp[1 << i][i] = 0 # 构建状态转移方程 for s in range(1, 1 << n): for i in range(n): if s & (1 << i) == 0: continue for j in range(n): if i == j or s & (1 << j) == 0: continue dp[s][i] = min(dp[s][i], dp[s ^ (1 << i)][j] + dist[j][i]) # 返回最终状态 return min(dp[(1 << n) - 1][i] + dist[i][0] for i in range(n)) # 示例 dist = [ [0, 2, 9, 10], [1, 0, 6, 4], [15, 7, 0, 8], [6, 3, 12, 0] ] print(tsp_dp(dist)) # 输出:21 ``` 在上面的代码中,我们首先使用$dp$数组记录子问题的最优解,然后通过状态转移方程逐步构建整个问题的最优解。 最后,我们通过计算$dp(\{1,2,\cdots,n\},1)$和从最后一个城市回到起点的距离之和的最小值来得到TSP问题的最优解。 总结 通过本文,我们学习了如何使用Python解决TSP问题动态规划算法。TSP问题是一个经典的组合优化问题,它的解决方法还有很多其他的算法,例如分支定界算法、遗传算法等。如果你对这些算法感兴趣,可以进一步学习相关的知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值