01背包问题从简单到复杂

题目:

有 N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的费用是 C i 1 ,得到
的价值是 W i 。求解将哪些物品装入背包可使价值总和最大。

状态转移方程:

F [i, v] = max {F [i − 1, v], F [i − 1, v − C i ] + W i }

基于递归的实现:

这里的出口容易搞错,出口可以从递推方程成立条件来考虑

def pack_0_1_Rec2(N,V,C,W):
    if N == 0 :
        return 0
    if V < C[N-1]:
        return pack_0_1_Rec(N-1,V,C,W)
    return max(pack_0_1_Rec(N-1,V,C,W),pack_0_1_Rec(N-1,V-C[N-1],C,W) + W[N-1])

基于第一种递归的自下而上和自上而下实现:

# 后来仔细考虑出后之后,做了如下修正:
def pack_0_1_Top_down(N,V,C,W):
    list = [[-1]*(V+1) for i in range(N+1)]
#    mins = min(C)
#    for i in range(N+1):
#        for j in range(V+1):
#            if i == 0 or j< mins:
#                list[i][j] =0
    list[0] = [0]*(V+1)

    
    def pack_0_1_Top_down_(N,V):
#        if list[N][V] == -1 and N >=1 and V >=mins:
        if list[N][V] == -1 and N >=1:
            A = pack_0_1_Top_down_(N-1,V)      
            if V < C[N-1]: 
                return A
            else:
                list[N][V] = max(A,pack_0_1_Top_down_(N-1,V-C[N-1])+W[N-1])
                
            
        return list[N][V]
    
    return pack_0_1_Top_down_(N,V)

def pack_0_1_bottom_up(N,V,C,W):
    list = [[-1]*(V+1) for i in range(N+1)]
    list[0] = [0]*(V+1)

    for i in range(1,N+1):
        for j in range(0,V+1):
            A = list[i-1][j]  
            if j < C[i-1]: 
                list[i][j] = A  
            else:
                list[i][j] = max(A,list[i-1][j-C[i-1]]+W[i-1])
#    print list      
    return list[N][V]

01背包问题的一维数组方式实现

凡是基于去与不去的问题,均可使用如下方式

def pack_0_1_first(N,V,C,W):
    def ZeroOnePack(F,ci,wi):
        for v in range(V,ci-1,-1):
            F[v] = max(F[v],F[v-ci] + wi)
        return F
    F =[0]*(V+1)
    
    for i in range(1,N+1):
        ZeroOnePack(F,C[i-1],W[i-1])
    return F[V]

01背包问题可行性问题

可行性问题,想清楚初始条件
如果是,要求恰好装满背包,那么在初始化时除了 F [0] 为 0 ,其
它 F [1…V ] 均设为 −∞ ,这样就可以保证最终得到的 F [V ] 是一种恰好装满背包的最优解。
如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将 F [0…V ] 全部设为 0 。

这是为什么呢?可以这样理解:初始化的 F 数组事实上就是在没有任何物品可以放入背包时的合法状态。
如果要求背包恰好装满,那么此时只有容量为 0 的背包可以在什么也不装且价值为 0 的情况下被“恰好装满”,
其它容量的背包均没有合法的解,属于未定义的状态,应该被赋值为-∞了。
如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为 0 ,
所以初始时状态的值也就全部为 0 了。
def pack_0_1_yes_or_no(N,V,C):
    def ZeroOnePack(F,ci):
        for v in range(V,ci-1,-1):
            F[v] = F[v-ci] or F[v]
        return F
    
    F =[-1]*(V+1)
    F[0] = True
    
    for i in range(1,N+1):
        ZeroOnePack(F,C[i-1])
        
    return F[V]

运行结果:

#%%
N = 6
V = 23
C = [1,3,4,5,17,11]
W = [2,9,7,5,11,4]
#%%
print pack_0_1_Rec(N,V,C,W)
print pack_0_1_first(N,V,C,W)
print pack_0_1_Top_down(N,V,C,W)
print pack_0_1_bottom_up(N,V,C,W)
print pack_0_1_Rec2(N,V,C,W)
print pack_0_1_yes_or_no(N,V,C)  

25
25
25
25
25
True

猜你喜欢

转载自blog.csdn.net/weixin_40759186/article/details/84107588