01背包问题----动态规划 -----python代码、优化

问题描述:

容量为C的背包选择装物品,有n个物品,它们有各自的体积wi和价值vi,如何让背包里装入的物品具有最大价值?

解题思路:

也就是n个物品选择装入背包,每个物品都有两种选择,是(1)或否(0),

建模:max(\sum xivi )       xi表示当前第i个物品是否选择,xi取值为(0,1)。

        约束条件:\sum xiwi <=C,选择的物品重量小于等于C,且这几样物品加起来的价值最大。

动态规划:

           动态规划算法通常用于求解具有某种最优性质的问题。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算。我们可以用一个表来记录所有已解的子问题的答案,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路

         适用动态规划的问题必须满足最优化原理和无后效性  。 最优化原理(最优子结构性质)

          最优化原理可这样阐述:一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。

          无后效性 将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。

           动态规划没有统一的处理方法,必须根据问题的各种性质并结合一定的技巧来处理,一般最常见的就是用公式求出每个位置的最大值,然后用二维列表保存,这是最基础的,之后再想办法优化。

01背包问题----动态规划 -----python代码、优化_第1张图片

         

递推关系:

1、V(i,j):当前背包容量 j,前 i 个物品最佳组合对应的最大价值

首先对当前第i个物品是否选择装,背包容量装不下当前物品,所以选择不装,所以当前最大价值就是前i-1个物品时的最大价值。V(i-1,j)

2如果背包容量可以装下当前的物品,就需要判断是否装。

max(不装v(i-1,j),  v_{ij-wi}),也就是不装时最大价值等于前i-1个物品时的最大价值,装就是最大价值等于装下的这个物品的价值加上背包容量为(j-wi)(减去当前这个物品的wi)时能装前i-1个物品时的最大价值(vij-wi),选取两者的最大值。

01背包问题----动态规划 -----python代码、优化_第2张图片

举例:4个物品:01背包问题----动态规划 -----python代码、优化_第3张图片 

 通过填表的方式:

01背包问题----动态规划 -----python代码、优化_第4张图片

 追踪解:

01背包问题----动态规划 -----python代码、优化_第5张图片

 如果当前i个物品的最大价值和前i-1个东西的最大价值一样时,说明第i个物品没放,则x;=0。反之当前i个物品的最大价值大于前i-1个物品的最大价值时,说明放了第i个物品,则x;=1。

01背包问题----动态规划 -----python代码、优化_第6张图片

 代码:(Python):

def bag(n, c, w, v):
    res = [[0 for j in range(c + 1)] for i in range(n + 1)]#初始值全赋值为0
    for i in range(1, n + 1):
        for j in range(1, c + 1):
            res[i][j] = res[i - 1][j]#根据公式填写表,得到最大收益
            if j >= w[i - 1] and res[i][j] < res[i - 1][j - w[i - 1]] + v[i - 1]:
                res[i][j] = res[i - 1][j - w[i - 1]] + v[i - 1]
    return res
def show(n, c, w, res):
    print('最大收益为:', res[n][c])
    x = [False for i in range(n)]
    j = c
    for i in range(1, n + 1):  # (1,6)
        if res[i][j] > res[i - 1][j]:#判断项目是否投资
            x[i - 1] = True
            j -= w[i - 1] #投资后(资金-当前金额投资)
    print('选择的项目为:')
    for i in range(n):
        if x[i]:
            print('第', i + 1, '个,', end='') #展示结果
    print('')
#实例
n = 5
c = 10
w = [2, 2, 6, 5, 4]
v = [6, 3, 5, 4, 6]
res = bag(n, c, w, v)
show(n, c, w, res)

利用动态规划解决此问题的效率即是填写此张表的效率,所以动态规划的 时间效率为O(number*capacity)=O(n*c),

由于用到二维数组存储子问题的解,所以动态规划的 空间效率为O(n*c);

 

优化,从上述的填表的过程中,我们可以发现vij的值只与上一行有关。

可以将V缩减成一维数组,从而达到优化空间的目的,状态转移方程转换为 B(j)= max{B(j), B(j-w(i))+v(i)}; 每一次推导V(i)(j)是通过V(i-1)(j-w(i))来推导的,所以要保留上一行前面的值。所以一维数组中j的扫描顺序应该从大到小(capacity到0),否者前一次循环保存下来的值将会被修改,从而造成错误。

 

你可能感兴趣的:(动态规划,算法,python)