zoukankan      html  css  js  c++  java
  • 动态规划-0-1背包问题

    0-1背包问题:有N件物品和一个容积为M的背包。第i件物品的体积w[i],价值是d[i]。
    求解将哪些物品装入背包可使价值总和最大。每种物品只有一件,可以选择放或者不放
    (N<=3500,M <= 13000)。
    思路:用 F[i][j] 表示取前i种物品,使它们总体积不超过j的最优取法取得的价值总和。要求F[N][M]
    就取1种物品,如果w[1]体积小于j,那么可以放入背包,价值为d[1],
    如果放不下,那么价值就是0。
    边界:if (w[1] <= j)
    F[1][j] = d[1];
    else
    F[1][j] = 0;
    用 F[i][j] 表示取前i种物品,使它们总体积不超过j的最优取法取得的价值总和
    递推: F[i][j] = max(F[i-1][j],F[i-1][j-w[i]]+d[i])
    取或不取第 i种物品,两者选优
    (j-w[i] >= 0才有第二项)
    本题如用记忆型递归,需要一个很大的二维数组,会超内存。注意到这个二维数组的下一行的值,
    只用到了上一行的正上方及左边的值,因此可用滚动数组的思想,只要一行即可。即可以用一维数组,
    用“人人为我”递推型动归实现。
    令V(i,j)表示当前背包为容量 j,前 i 个物品最佳组合对应的价值最优解,那么就有两种可能:
    1.包的容量比该商品体积小,即j < Vi,装不下第i个物品。此时背包容量为j,前i个物品的最优解
    与背包容量为j,前i-1个物品最佳组合对应的最优解是一样的,即V(i,j)=V(i-1,j)
    2.当包的容量等于或大于该商品的体积,即j >= Vi,能装下第i个物品。那无非有两种情况,
    在前i个物品,背包容量为j的最优组合里有第i个物品,但同时占据了背包Wj个容量,
    V(i,j) = V(i-1,j-Wi)+Vi;

    通过上面的方法可以求出背包问题的最优解,但还不知道这个最优解由哪些商品组成,故要根据
    最优解回溯找出解的组成,根据填表的原理可以有如下的寻解方式:
    V(i,j)=V(i-1,j)时,说明没有选择第i 个商品,则回到V(i-1,j);
    V(i,j)!=V(i-1,j)时,说明装了第i个商品,该商品是最优解组成的一部分,
    一定有V(i,j)=V(i-1,j-w(i))+v(i),随后我们得回到装该商品之前,即回到V(i-1,j-w(i));
    重复1,2,一直遍历到i=0结束为止,所有解的组成都会找到。

    参考:https://blog.csdn.net/ggdhs/article/details/90648890
    https://blog.csdn.net/qq_34178562/article/details/79959380

    python算法实现:

     1 # 该方法有一个弊端,比如体积是10,我们的列就从0至10去做循环,这样如果题目要求有13000这样的
     2 # 数值,二维数组太大了,运行效率极低,甚至可能出现内存不足的情况,因此需要改进算法
     3 def zeroOneBag1(num, capacity, weightList, valueList):
     4     # 二维数组初始化,行表示物品,列表示最大价值
     5     # valueExcel[0][j],valueExcel[i][0]边界条件都是0
     6     # valueExcel[0][j]表示前0种物品,那就是没有选择物品,所以最大价值是0
     7     # valueExcel[i][0]表示背包总容量是0,那就是什么物品都没法装入,因此价值也是0
     8     valueExcel = [[0 for j in range(capacity + 1)] for i in range(num + 1)]
     9     for i in range(1, num + 1):
    10         for j in range(1, capacity + 1):
    11             # 不选该物品的情况下,等于同列上一行的值
    12             valueExcel[i][j] = valueExcel[i-1][j]
    13             # 背包总容量能够放当前物体,那么就需要用总容量-当前物品的体积,得到剩余的体积,
    14             # 那么valueExcel[i-1][j-weightList[i-1]]表示没有加入该物品,该剩余体积值下的最大价值
    15             # 加上该物品的价值后,那就是valueExcel[i-1][j-weightList[i-1]]+valueList[i-1]
    16             # 表示加入该物品后,在该体积下的最大价值,这时为了去整体的最优,就需要与不加入该物品时,
    17             # 那两个的价值是最大的,然后填入该二维数组内,已保证二维数组中的值都是在[i][j]下都是最优解
    18             if j >= weightList[i-1]:
    19                 valueExcel[i][j] = max(valueExcel[i][j], valueExcel[i-1][j-weightList[i-1]]+valueList[i-1])
    20 
    21     #for x in valueExcel:
    22         #print(x)
    23 
    24     return valueExcel
    25 
    26 
    27 # 背包价值最大化的情况下,选择了哪些物品
    28 
    29 def show(num, capacity, weightList, valueExcel):
    30     print('最大价值为:', valueExcel[-1][-1])
    31     # 是否选择该物品的list,[False, False, False, False, False, False]
    32     x = [False for i in range(num)]
    33     j = capacity
    34     for i in range(num, 0, -1):
    35         # 如果valueExcel[i][j]大于上一行同列的值,说明选择了i行物品
    36         if valueExcel[i][j] > valueExcel[i - 1][j]:
    37             x[i - 1] = True
    38             j -= weightList[i - 1]
    39     print('背包中所装物品为:')
    40     for i in range(num):
    41         if x[i]:
    42             print('', i+1, '个,', end='')
    43 
    44 # valueExcel可以利用递推,实现只需要一维数组即可,思路如下:
    45 # valueExcel[i][j]值替换valueExcel[i-1][j]的值,这样从而保证就一维数组即可
    46 # 计算valueExcel[i][j]值可能需要比j小的值,也就是valueExcel[i-1][<j],因此
    47 # 我们递推数组的时候,必须从右边开始递推
    48 # 利用递推后,因为没存储中间结果,因此就没法找出最大价值下选择了哪些物品
    49 def zeroOneBag2(num, capacity, weightList, valueList):
    50     valueExcel = [0 for i in range(capacity + 1)]
    51     for i in range(1, num + 1):
    52         for j in range(capacity, 0, -1):
    53             # 背包总容量够放当前物体,遍历前一个状态考虑是否置换,这里的value[j]即为上一次最佳结果
    54             if j >= weightList[i-1]:
    55                 valueExcel[j] = max(valueExcel[j-weightList[i-1]]+valueList[i-1], valueExcel[j])
    56 
    57     print(valueExcel)
    58     return valueExcel[-1]
    59 
    60 def main():
    61     # 多少个物品
    62     num = 6
    63     # 背包的总容量
    64     capacity = 10
    65     # 每个物品的重量
    66     weightList = [2, 2, 3, 1, 5, 2]
    67     # 每个物品的价值
    68     valueList = [2, 3, 1, 5, 4, 3]
    69     valueExcel = zeroOneBag1(num, capacity, weightList, valueList)
    70     show(num, capacity, weightList, valueExcel)
    71     #print("物品装入背包可使价值总和最大为:%d"%maxValue)
    72 
    73 if __name__ == '__main__':
    74     main()
  • 相关阅读:
    存储结构接收数组
    oracle数据库sql根据查看执行计划优化sql--走不走索引
    多线程--Thread
    java常用集合族谱
    设计模式之二 适配模式
    Tomcat优化问题
    设计模式之一
    C++虚函数表,虚表指针,内存分布
    设计模式
    linux环境下的时间编程
  • 原文地址:https://www.cnblogs.com/an-wl/p/13152739.html
Copyright © 2011-2022 走看看