动态规划——经典案例分析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]
def knapsack_01(weights, values, W):
    n = len(weights)
    dp = [[0] * (W + 1) for _ in range(n + 1)]

    for i in range(1, n + 1):
        for j in range(1, W + 1):
            if weights[i - 1] > j:
                dp[i][j] = dp[i - 1][j]
            else:
                dp[i][j] = max(dp[i - 1][j], values[i - 1] + dp[i - 1][j - weights[i - 1]])

    return dp[n][W]

# 测试
weights = [2, 3, 4, 5]
values = [3, 4, 5, 6]
W = 5
result = knapsack_01(weights, values, W)
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 可以是任意一个经过的城市。
def tsp(graph):
    n = len(graph)
    dp = [[float('inf')] * n for _ in range(1 << n)]
    dp[1][0] = 0

    for mask in range(1, 1 << n):
        for u in range(n):
            if (mask >> u) & 1:
                for v in range(n):
                    if u != v and (mask >> v) & 1:
                        dp[mask][u] = min(dp[mask][u], dp[mask ^ (1 << u)][v] + graph[v][u])

    mask = (1 << n) - 1
    u = 0
    min_dist = min(dp[mask][v] + graph[v][u] for v in range(1, n))

    return min_dist

# 测试
graph = [
    [0, 29, 20, 21],
    [29, 0, 15, 17],
    [20, 15, 0, 18],
    [21, 17, 18, 0]
]
result = tsp(graph)
print(result)  # 输出 85

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

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


案例六:流水线调度问题

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

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

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

  1. 初始化两个数组 T1T2,分别记录第一条流水线和第二条流水线的到达每个工位的最短时间,初始化数组 L1L2 分别记录前一条流水线和后一条流水线的前一工位是哪一个。
  2. 逐个遍历各个工位,根据当前工位的加工时间和转移时间,更新 T1T2 数组。
  3. 找到总时间最短的最后一个工位,然后根据 L1L2 数组回溯找到最短路径。
def assembly_line_scheduling(a, t, e, x):
    n = len(a)
    T1 = [0] * n
    T2 = [0] * n
    L1 = [0] * n
    L2 = [0] * n

    T1[0] = e[0] + a[0][0]
    T2[0] = e[1] + a[1][0]

    for i in range(1, n):
        if T1[i - 1] <= T2[i - 1] + t[1][i - 1]:
            T1[i] = T1[i - 1] + a[0][i]
            L1[i] = 1
        else:
            T1[i] = T2[i - 1] + t[1][i - 1] + a[0][i]
            L1[i] = 2

        if T2[i - 1] <= T1[i - 1] + t[0][i - 1]:
            T2[i] = T2[i - 1] + a[1][i]
            L2[i] = 2
        else:
            T2[i] = T1[i - 1] + t[0][i - 1] + a[1][i]
            L2[i] = 1

    if T1[n - 1] + x[0] <= T2[n - 1] + x[1]:
        F = T1[n - 1] + x[0]
        L = 1
    else:
        F = T2[n - 1] + x[1]
        L = 2

    path = [L]
    for i in range(n - 1, 0, -1):
        if L == 1:
            L = L1[i]
        else:
            L = L2[i]
        path.insert(0, L)

    return F, path

# 测试
a = [[7, 9, 3, 4, 8], [8, 5, 6, 4, 5]]
t = [[2, 3, 1, 3], [2, 1, 2, 2]]
e = [2, 4]
x = [3, 2]

min_time, path = assembly_line_scheduling(a, t, e, x)
print(f"最短时间:{min_time}")
print(f"最短路径:{path}")

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恭仔さん

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值