0/1背包问题--Dynamic Programming

DP问题的特征:

  1. 重复子问题
  2. 存在最优子集

背包问题属于经典的DP问题,而0/1背包问题是属于最简单的情况。

0/1的意思是每种物品只有一件,要么放入背包中,要么不放

问题定义:

有N个物品,要放入容量为W的背包中,第i件物品重量为w(i),

价值为v(i),问要怎样放才能在不超过背包容量的基础上,获得最大的价值。

算法描述:

需要用到递归的思想,定义A(i, j)为前i个物品中在容量为j的情况下所能

达到的最大价值,则A(0,j) = 0,A(i,0) = 0(i <= N and j <= W).

如果w(i) > j时,说明第i件物品不能放入背包中,价值不变,所以A(i, j) = A(i - 1, j);

如果w(i) < j时,说明第i件物品能入入背包中,那么又有两种情况,选择放入或者是

不放,如果选择不放入,那么价值还是不变,A(i, j) = A(i - 1, j);如果选择放入,

A(i, j) = v(i) + A(i - 1, j - w(i)),到底选择放入还是不放入,需要取这两种选择的最大值。


用数学表达式可以表示为:

代码

版本1:

[python] view plaincopy
  1. def B(w, v, i, j):  
  2.     if i == 0:  
  3.         if w[i] <= j:  
  4.             return v[i]  
  5.         else:  
  6.             return 0  
  7.     without_i = B(w, v, i - 1, j)  
  8.     if w[i] > j:  
  9.         return without_i  
  10.     else:  
  11.         with_i = v[i] + B(w, v, i - 1, j - w[i])  
  12.     return max(without_i, with_i)  
  13.       
  14. if __name__ == "__main__":  
  15.     w = [23456]#list of item's weight  
  16.     v = [14368]#list of item's value  
  17.     i = len(w) - 1     #number of items  
  18.     j = 12             #max weight constraints  
  19.     print B(w, v, i, j)  

版本1的实现按照算法的思路可以解决问题但是时间复杂度为指数级,当i取大一点

值时,需要运算很久。这也是一些递归算法存在的问题,这个时候,需要用一种叫做

memorization的技术,也就是储存中间值的方法,纪录之前已经算好的子问题的值,不

用重新计算,直接拿来用就好。

版本二:

[html] view plaincopy
  1. m = {}#memo of value of subproblem, m[(i,j)] = maxValue  
  2.   
  3. def fastB(w, v, i, j):  
  4.     global m  
  5.     try:  
  6.         return m[(i, j)]  
  7.     except KeyError:  
  8.         if i == 0:  
  9.             if w[i] <= j:  
  10.                 m[(i, j)] = v[i]  
  11.                 return v[i]  
  12.             else:  
  13.                 m[(i, j)] = 0  
  14.                 return 0  
  15.     without_i = fastB(w, v, i - 1, j)  
  16.     if w[i] > j:  
  17.         m[(i, j)] = without_i  
  18.         return without_i  
  19.     else:  
  20.         with_i = v[i] + fastB(w, v, i - 1, j - w[i])  
  21.     res = max(without_i, with_i)  
  22.     m[(i, j)] = res  
  23.     return res  
  24.       
  25. if __name__ == "__main__":  
  26.     w = [2, 3, 4, 5, 6]  
  27.     v = [1, 4, 3, 6, 8]  
  28.     i = len(w) - 1  
  29.     j = 12  
  30.     print fastB(w, v, i, j)  
版本二,因为加入储存中间值的变量B,实际运行中,速度大大提高,时间复杂度接近

线性,用空间换时间的方法。

附加问题:

在求出最大值的同时,需要返回具体的物品最优列表

算法描述:

这个问题需要使用到上一个问题中得到的m字典,从最后一个结果往前算,范围为[n-1,1],当m[(i, j)] > m[(i - 1, j)]时,把物品i放入最优列表中,接着j = j - w(i), 继续往前比,比完之后,最后决定第0个物品是否要加入背包,当最优列表中所有物品重量再加上第0个物品的重量不超过容量限制时,把第0个物品添加到最优列表中,否则不加入

代码:

[python] view plaincopy
  1. def findOptimalSub(m, w, i, j):  
  2.     res = []  
  3.     maxWeight = j  
  4.     sum = 0  
  5.       
  6.     for index in range(i, 0, -1):  
  7.         if m[(index, j)] > m[(index - 1, j)]:  
  8.             res.append(index)  
  9.             j = j - w[index]  
  10.       
  11.     for item in res:  
  12.         sum = sum + w[item]  
  13.       
  14.     if maxWeight - sum >= w[0]:  
  15.         res.append(0)  
  16.     return res  

posted on 2012-09-25 22:28  linzuxin  阅读(181)  评论(0编辑  收藏  举报

导航