苏苏酱陪你学动态规划(三)——揹包问题

1、0-1揹包问题

       揹包问题的典型描述如下:

       一、假如你是奥特曼,有N点的攻击力,现在有一些小怪兽,每一个小怪兽被打倒都需要消耗你一定数量的攻击力,同时你将获得该怪兽对应的金币。现在请计算你所能获得的最多金币数。

       二、加入你是小偷,带了一个容量为V的包,此时你去商店偷商品(每一个商品都具有不同的价值),每偷一个商品将占用你揹包一定的空间,请你偷价值尽量多的商品。

2、问题的分析

        这一类问题都是0-1揹包问题,换句话说,每一个问题中都可以抽象为“偷”或者“不偷”,以此来达到目标函数最大化。这种问题的求解通常使用动态规划。

        假设已知x个商品它们的体积存储在数组L中;

       假设已知x个商品它们的价值存储在数组P中;

       假设揹包最大容积是V,那么我们的状态函数就是B(x,V)——表示从x个商品中偷使得获得价值最多,此时的揹包容积为V。

       现在我们可以从后往前推:

       如果最后一个商品可以被偷(包还装得下),那么一定偷这个商品(多偷一个是一个,这是常识)

       如果最后一个商品的体积大于揹包剩下的容积,显然我们已经无法偷这个商品,那么此时的状态函数为B(x-1,V)

       现在剩下一个新的问题,那就是剩下的倒数第二个商品是偷好,还是不偷好,也就是偷和不偷哪个可以使最终的收益最大?

       一、偷,那么此时的状态函数为B(x-1,V-L[x-1])+P[x-1]

       二、不偷,那么此时的状态函数B(x-1,V)

       因此,通过上述分析,我们就知道了状态的转移函数,那么一直运算到边界条件,就可以返回求出最优解,这是动态规划中典型的分而治之的思想,也就是把大问题分解成若干小问题求解,最终获得最优解。

       为了避免同一个子问题的反复求解,我们一般使用记忆化递归的方法,记录每一个子问题的解,然后记录在一个数组中,这样可以控制算法的时间空间复杂度。

3、编程实现

loupan, zijin = map(int,input().split())
L = [0]
P = [0]
for i in range(loupan):
    l, p = map(int,input().split())
    L.append(l)
    P.append(p)
B = [[0]*(zijin+1)]*(loupan+1)
print("B:",B)
print("L:",L)
print("P:",P)
for c in range(1,loupan+1):
    for r in range(1,zijin+1):
        if r < L[c]:
            B[c][r] = B[c-1][r]
        else:
            B[c][r] = max(B[c-1][r],B[c-1][r-L[c]] + P[c])
print(B[loupan][zijin])

 

点赞