当前位置:   article > 正文

动态规划——经典案例分析2_动态规划案例解析

动态规划案例解析

目录

案例四:0-1背包问题

案例五:货郎担问题

案例六:流水线调度问题


动态规划(Dynamic Programming)是一种解决复杂问题的算法设计思想,通常用于解决具有重叠子问题和最优子结构性质的问题。它将问题分解成多个小问题,通过解决小问题来解决原始问题。

案例分析一:动态规划——经典案例分析

案例四:0-1背包问题

给定一组物品,每个物品有一个重量和一个价值,同时给定一个背包容量限制。目标是选择一些物品放入背包中,使得在满足背包容量限制的前提下,所选择的物品的总价值最大。

方法:可以使用一个二维数组 dp,其中 dp[i][j] 表示在前 i 个物品中,背包容量为 j 时的最大价值。具体的算法如下:

  1. 初始化一个 (n+1)*(W+1) 的二维数组 dp,其中 n 是物品的个数,W 是背包的容量。
  2. 逐个遍历物品,对于第 i 个物品:
    • 如果 weights[i] 大于当前背包容量 j,则无法放入背包,dp[i][j] 等于上一个物品在相同容量下的最大价值,即 dp[i-1][j]
    • 否则,可以选择放入或不放入当前物品。如果选择放入,那么最大价值就是当前物品的价值 values[i] 加上剩余容量下放入前 i-1 个物品的最大价值 dp[i-1][j-weights[i]];如果选择不放入,那么最大价值就是放入前 i-1 个物品的最大价值 dp[i-1][j]
    • 取两者中的最大值作为当前状态的最大价值,即 dp[i][j] = max(dp[i-1][j], values[i] + dp[i-1][j-weights[i]])
  3. 最终的最大价值就是 dp[n][W]
  1. def knapsack_01(weights, values, W):
  2. n = len(weights)
  3. dp = [[0] * (W + 1) for _ in range(n + 1)]
  4. for i in range(1, n + 1):
  5. for j in range(1, W + 1):
  6. if weights[i - 1] > j:
  7. dp[i][j] = dp[i - 1][j]
  8. else:
  9. dp[i][j] = max(dp[i - 1][j], values[i - 1] + dp[i - 1][j - weights[i - 1]])
  10. return dp[n][W]
  11. # 测试
  12. weights = [2, 3, 4, 5]
  13. values = [3, 4, 5, 6]
  14. W = 5
  15. result = knapsack_01(weights, values, W)
  16. print(result) # 输出 11

案例五:货郎担问题

Traveling Salesman Problem,假设有一个货郎要从起始点出发,经过一系列的城市,最终回到起始点。每两个城市之间都有一个距离,货郎希望找到一条路径,使得他经过的城市总距离最短。

方法:货郎担问题是一个NP难问题,没有多项式时间的解法,但可以使用动态规划来近似解决。一种常用的方法是使用状态压缩动态规划。具体的算法如下:

  1. 将问题抽象成一个图,其中节点表示城市,边表示城市间的距离。
  2. 定义一个二维数组 dp[mask][i],其中 mask 代表经过的城市的集合(用二进制表示,第 i 位为1表示经过第 i 个城市),i 表示当前所在的城市。
  3. 使用状态转移方程进行动态规划:
    • dp[mask][i] = min(dp[mask ^ (1 << j)][j] + distance(i, j)),其中 j 表示未经过的城市,distance(i, j) 表示从城市 i 到城市 j 的距离。
    • 这里的 mask ^ (1 << j) 表示将第 j 位取反,表示加入了城市 j
    • dp[mask][i] 表示在经过 mask 中的城市后,以 i 为结束城市的最短路径长度。
  4. 最终的最短路径长度就是 min(dp[(1 << n) - 1][i] + distance(i, 0)),其中 n 是城市的数量,i 可以是任意一个经过的城市。
  1. def tsp(graph):
  2. n = len(graph)
  3. dp = [[float('inf')] * n for _ in range(1 << n)]
  4. dp[1][0] = 0
  5. for mask in range(1, 1 << n):
  6. for u in range(n):
  7. if (mask >> u) & 1:
  8. for v in range(n):
  9. if u != v and (mask >> v) & 1:
  10. dp[mask][u] = min(dp[mask][u], dp[mask ^ (1 << u)][v] + graph[v][u])
  11. mask = (1 << n) - 1
  12. u = 0
  13. min_dist = min(dp[mask][v] + graph[v][u] for v in range(1, n))
  14. return min_dist
  15. # 测试
  16. graph = [
  17. [0, 29, 20, 21],
  18. [29, 0, 15, 17],
  19. [20, 15, 0, 18],
  20. [21, 17, 18, 0]
  21. ]
  22. result = tsp(graph)
  23. print(result) # 输出 85

在这个例子中,我们使用了状态压缩动态规划来解决货郎担问题。graph 表示城市之间的距离,函数 tsp 返回从起始城市出发,经过所有城市后回到起始城市的最短路径长度。

需要注意的是,货郎担问题是一个组合优化问题,随着城市数量的增加,问题的复杂度会急剧增加,因此实际应用中可能需要使用一些启发式算法来近似解决


案例六:流水线调度问题

假设有两条流水线,每条流水线有多个工位,产品从第一条流水线的第一个工位开始,然后经过各个工位,最终到达第二条流水线的最后一个工位。每个工位都有一个加工时间,表示在该工位上加工一个产品所需的时间。

另外,产品在两条流水线之间转移的时间也是固定的。目标是找到一条合适的生产路径,使得生产一个产品所需的总时间最短。

方法:可以使用两个数组 T1T2 来记录从起始点到当前工位的最短时间,同时使用两个数组 L1L2 来记录前一条流水线和后一条流水线的前一工位是哪一个。具体的算法如下:

  1. 初始化两个数组 T1T2,分别记录第一条流水线和第二条流水线的到达每个工位的最短时间,初始化数组 L1L2 分别记录前一条流水线和后一条流水线的前一工位是哪一个。
  2. 逐个遍历各个工位,根据当前工位的加工时间和转移时间,更新 T1T2 数组。
  3. 找到总时间最短的最后一个工位,然后根据 L1L2 数组回溯找到最短路径。
  1. def assembly_line_scheduling(a, t, e, x):
  2. n = len(a)
  3. T1 = [0] * n
  4. T2 = [0] * n
  5. L1 = [0] * n
  6. L2 = [0] * n
  7. T1[0] = e[0] + a[0][0]
  8. T2[0] = e[1] + a[1][0]
  9. for i in range(1, n):
  10. if T1[i - 1] <= T2[i - 1] + t[1][i - 1]:
  11. T1[i] = T1[i - 1] + a[0][i]
  12. L1[i] = 1
  13. else:
  14. T1[i] = T2[i - 1] + t[1][i - 1] + a[0][i]
  15. L1[i] = 2
  16. if T2[i - 1] <= T1[i - 1] + t[0][i - 1]:
  17. T2[i] = T2[i - 1] + a[1][i]
  18. L2[i] = 2
  19. else:
  20. T2[i] = T1[i - 1] + t[0][i - 1] + a[1][i]
  21. L2[i] = 1
  22. if T1[n - 1] + x[0] <= T2[n - 1] + x[1]:
  23. F = T1[n - 1] + x[0]
  24. L = 1
  25. else:
  26. F = T2[n - 1] + x[1]
  27. L = 2
  28. path = [L]
  29. for i in range(n - 1, 0, -1):
  30. if L == 1:
  31. L = L1[i]
  32. else:
  33. L = L2[i]
  34. path.insert(0, L)
  35. return F, path
  36. # 测试
  37. a = [[7, 9, 3, 4, 8], [8, 5, 6, 4, 5]]
  38. t = [[2, 3, 1, 3], [2, 1, 2, 2]]
  39. e = [2, 4]
  40. x = [3, 2]
  41. min_time, path = assembly_line_scheduling(a, t, e, x)
  42. print(f"最短时间:{min_time}")
  43. print(f"最短路径:{path}")

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/798930
推荐阅读
相关标签
  

闽ICP备14008679号