0-1背包问题【动态规划/跳跃点算法】

动态规划

已知

  • n 个物品
  • w[i] 为第i个物品的重量
  • v[i] 为第i个物品的价值 (i=1,2,3……n)
  • W 为背包容量

设定

  • j 为背包剩余容量
  • m(i,j) 代表背包余量j,从第一个物品到第i个物品的最优价值
  • x[i]=0||1
    x[i]值为0代表背包不装入第i个物品,值为1代表装入第i个物品

分析

不装入第i个物品时的价值:m(i-1,j)

m(i−1,j-w[i]x[i]) 由于x[i]=0,所以该式化简为:m(i-1,j)

装入第i个物品得到的价值:m(i-1,j-w[i]x[i])+v[i]

步骤

1.w[i] 记录第i个商品的载重量,v[i] 记录第i个商品对应的收益,m[i,j]为从第一个到第i个商品的最优价值
2.如果背包剩余容量j小于当前商品重量w[i],则商品无法放入背包,收益不变, 否则比较装入该商品和不装该商品,哪种情况获得的收益更大,记录最大收益值
3,对每个商品的从背包的1 到 W的承重情况循环步骤2,得到对应的最大收益,

图解过程见下链接:
01背包问题 动态规划
01背包问题

python代码

N = 5       #商品的种类
W = 11      #背包的承重
w = [0,1,2,5,6,7]    #商品的承重,不使用 w[0]
v = [0,1,6,18,22,28]  # 商品的价值,不使用 v[0]

# 创建一个大小为(W+1)*(N+1)的二维列表,记录统计数据
result = [[0 for col in range(W+1)] for row in range(N+1)]
a= [0 for b in range(W+1)]
print(a)
# 设置初始值为0的X列表,代表背包中为空的状态
#X = [0] * N
# 动态规划算法解决01背包问题
def knapsack():

    # 逐个遍历每个商品
    for i in range(1, N + 1):

        # 求出从 1 到 W 各个承重对应的最大收益
        for j in range(1, W + 1):

            # 如果背包承重小于商品总重量,则该商品无法放入背包,收益不变
            if j < w[i]:
                result[i][j] = result[i - 1][j]

            # 比较装入该商品和不装该商品,哪种情况获得的收益更大,记录最大收益值
            else:
                result[i][j] = max(result[i - 1][j], v[i] + result[i - 1][j - w[i]])





# 追溯选中的商品
def selection():
    a=W
    i=N
    while i>0:

        if result[i][a]!=result[i-1][a]:
            #X[i-1]=1

            print(" 第%i个商品,质量-价值 序列为(%d,%d) "%(i,w[i],v[i]))
            a=a-w[i]
        # else:
        #     X[i-1]=0
        i-=1

knapsack()

print("最大收益为 %d" % (result[N][W]))
print("装入背包的商品为:")
selection()

参考链接:http://c.biancheng.net/algorithm/01-knapsack.html

补充知识点(踩的坑)

创建一个大小为(W+1)*(N+1)的二维列表,记录统计数据
result = [[0 for col in range(W+1)] for row in range(N+1)]
不能写成
result = [[0] * (W+1)] * (N+1)
因为[0] (W+1)是一个一维数组的对象, (N+1)的话只是把对象的引用复制了(N+1)次
但是创建一位数组两种方法都可以
a= [0 for b in range(W+1)]
a=[0] * (W+1)
参考:https://www.cnblogs.com/coderzh/archive/2008/05/18/1201993.html

跳跃点算法

设定

p(i)代表m(i,j)的跳跃点点集
q(i)代表 m(i,j-w[i])+v[i]的跳跃点点集合

1.其中q(i) 可由p(i)的每一个点加上(w[i],v[i])得到。
2. p ( i − 1 )并 上 q ( i − 1 )然后减去受控跳跃点(即必然不合法的点,同 w 下 v 非最大的点和w较大但是v较小,还有)即为 p(i)
即跳跃点图必须是阶梯上升的

分析

设定:
W=8
wi=3,8,4
vi=6,9,5

p[0]={(0,0)}
p[1]={(0,0),(3,6)}
p[2]={ p[1] ∪ ( p[1] ⊕ (w[1],v[1]) )}
={(0,0),(3,6),(8,9)}
同理:
p[3]={(0,0),(3,6)(7,11)}

步骤

1.w[i] 记录第i个商品的载重量,v[i] 记录第i个商品对应的收益,m[i,j]为从第一个到第i个商品的最优价值.p[i]代表m(i,j)的跳跃点点集,q[i]代表 m(i,j-w[i])+v[i]的跳跃点点集合
2.对于不装当前商品的所有装包情况集合p,每种情况加上该商品的承重和价值后,若该情况的所需容量小于背包容量,则并入集合q .否则丢弃,得到装上当前商品的所有情况集合即q[i-1],然后两种情况的集合合并,除去受控点,得到不装下一个商品的所有装包情况集合即p[i]
3.对每个商品种类循环步骤2,最后可得到装入背包的最优值
4.记录好表之后,从终态开始往回倒找解路径即可。

代码

#跳跃点算法
w = [3,5,6,8,9,11]   #商品的承重
v = [7,12,18,22,25,27]  # 商品的价值
N = len(w)       #商品的种类
Capacity = 19    #背包的承重

def merge_points(p,q):
    p_len=len(p)
    q_len=len(q)
    # print(q)
    # print(q_len)
    i=j=0
    merge_points =[]
    while i<p_len and j<q_len:

        if p[i][0]<q[j][0]:
            merge_points.append(p[i])
            if p[i][1]>=q[j][1]:
                j+=1
            i += 1
        else:
            merge_points.append(q[j])
            if p[i][1]<=q[j][1]:
                i += 1
            j+=1
    while i<p_len:
        if p[i][0]>merge_points[-1][0] and p[i][1]>merge_points[-1][1]:
            merge_points.append(p[i])
        i+=1
    while j<q_len:
        if q[j][0]>merge_points[-1][0] and q[j][1]>merge_points[-1][1]:
            merge_points.append(q[j])
        j+=1

    return merge_points
def knapspack2():
    jump_p=[[] for b in range(N+1)]
    jump_q=[[] for a in range(N+1)]
    jump_p[0].append((0,0))
    for i in range(1,N+1):
        jump_q[i-1]=[(point[0]+w[i-1],point[1]+v[i-1]) for point in jump_p[i-1] if point[0]+w[i-1]<=Capacity]

        jump_p[i]=merge_points(jump_p[i-1],jump_q[i-1])

    return jump_p

def selection():
    vector =[0 for x in range(N)]
    point =result[N][-1]
    for i in range(N,0 ,-1):
        temp_p=(point[0]-w[i-1],point[1]-v[i-1])
        if temp_p in result[i-1]:
            print(" 第%d个商品,质量-价值 序列为(%d,%d) "%(i,w[i-1],v[i-1]))
            vector[i-1]=1
            point =temp_p




result =knapspack2()

print("最大收益为:",result[N][-1][1])
print("装入背包的商品为:")
selection()

链接

算法:0-1背包(跳跃点解法)

posted @ 2022-11-09 17:07  B0t1  阅读(766)  评论(0编辑  收藏  举报