0-1背包问题:动态规划 python 空间优化

0-1背包问题总述

0-1背包问题是最简单的问题,此外还要完全背包问题、多重背包问题、混合背包问题、二维费用背包问题、分组背包问题等等。好的参考资料可以见《背包问题九讲》:https://www.kancloud.cn/kancloud/pack/70124

其中0-1背包问题是最基本的问题,其问题描述如下:

给出n个珍珠的体积v[i]和其价值price[i],将他们装入一个大小为C的背包,最多能装入的总价值有多大?

之所以叫0-1背包,就是因为n个物品,每个只有一件,只有装包/不装包两种状态,即0-1问题。

解决该问题的方法为动态规划(Dynamic Programming),动态规划单听这个词觉得很庞大,其实可以说是对递归的一种优化,递归是不管需不需要都进行重新计算,其计算复杂度极高,动态规划用数据来存储其之前的状态,当进行下一步计算的时候直接调用不需要重新计算。那么问题来了,动态规划需要确定两个点:状态、状态转移方式。这里可以参考很多资料,不再仔细叙述。

状态转移方式:dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i]] +price[i]);

最后计算出结果后,还要把哪些东西放入背包可以打印出来,这个利用倒循化进行遍历,如果后一个物品放入后价值高,则确定是要放入的;然后更新当前的体积。

二维数据解决0-1背包问题

二维空间的python代码如下:

# 将珠宝装入背包,珠宝Jewelry有体积、价值(v,price)
# 背包总容量为C,问题:如何将最有价值的珠宝装进去
import numpy as np
def pack():
    C=10
    num=5
    v =   [4,3,5,2,5]
    price=[9,6,1,4,1] # 初始定义好的东西
    sum=np.zeros((num+1,C+1)) # 相当于申请二维的空间
    for jew in range(num+1): # 珠宝个数
        for c in range(C+1): # 容量大小
            if(jew==0):    # 如果没有装入珠宝
                sum[jew][c]=0
            else:
                sum[jew][c]=sum[jew-1][c]
            if(jew>0 and c>v[jew-1]):
                # d[i][j] >?= d[i - 1][j - v[i - 1]] + price[i - 1];
                sum[jew][c]=max(sum[jew-1][c],sum[jew-1][c-v[jew-1]]+price[jew-1])
    print("the max price:",sum[num][C])

    # 记录最终的装入背包的珠宝,并打印 pack[]
    pack=np.zeros((num))
    volume = C
    print(pack)
    for jew in range(num,-1,-1):  # 倒叙遍历(循环)
        if(sum[jew][volume]>sum[jew-1][volume]):  # 如果加入最后一个比前一个价值要高
            pack[jew-1]=1
            volume=volume-v[jew-1]

    print("珠宝装包情况",pack)
    # print(C)
    # print(sum)

if __name__=="__main__":
    pack()

结果为:

0-1背包问题:动态规划 python 空间优化_第1张图片

最后一个矩阵为所有的记录,打印出来方便理解。

空间优化:一维数据解决0-1背包

从代码以及动态规划的角度可以很明显的感觉到,时间复杂度是无法优化的,但当前状态dp[i]只与前一个状态dp[i-1]相关,所以其空间复杂度可以进一步优化。那么其核心问题就变为:如果只用一个数组dp[0...V],能不能保证第i次循环结束后dp[v]就是之前定义的状态dp[i][v]呢?dp[i][v]是由dp[i-1][v]和dp[i-1][v-c[i]]两个子问题递推而来,能否保证在推dp[i][v]时(也即在第i次主循环中推f[v]时)能够得到dp[i-1][v]和dp[i-1][v-c[i]]的值呢?事实上,在每次主循环中我们以v=V...0的逆序推dp[v],这样才能保证推dp[v]时dp[v-c[i]]保存的是状态dp[i-1][v-c[i]]的值。伪代码如下:

for i  in 0 ... N
    for  v = V ... 0
        dp[v] = max{dp[v], dp[v-v[i]] + price[i]}

所以,其一维数据的代码为:

def find1array():
    C = 10  # 背包总体积
    num = 5 # 物品个数
    v   =   [4, 3, 5, 2, 5] # 每个物品体积
    price = [9, 6, 1, 4, 1]  # 初始定义好的价格
    dp=[0 for i in range(C+1)] # 定义固定大小()的数组
    for i in range(num): # 从第i个物品开始遍历
        for j in range(C,v[i]-1,-1): # 从容量开始往下递减
            dp[j]=max(dp[j],dp[j-v[i]]+price[i])

    print("一维递归计算结果:",dp[C])

 

看后欢迎点赞。哈哈

 

你可能感兴趣的:(算法-思维)