码迷,mamicode.com
首页 > 其他好文 > 详细

————2020.1.17————

时间:2020-01-17 23:19:18      阅读:87      评论:0      收藏:0      [点我收藏+]

标签:定义   拆分   void   else   san   表示   维数   mic   个数   

# 算法 || 背包问题总结 #


1、0 1背包。

 

何谓0-1背包,可以这样想,那里有一堆值钱的东西,每一样东西只有一件,他们的价值和体积都不一样,现在要你从这n件里面挑选一些放到一个容量一定的背包里面,使得你的背包里的东西总价值最大。对于这些东西的每一件,你可以选择放进你的背包或者是不放进去。(这里放与不放就对应着两种状态0,1),所以称之为0-1背包。

规定f[i][v]来表示前i件物品放入到容量为v的背包里所获得的最大总价值,c[i]为第i件物品的体积,w[i]表示第i件物品的价值

转移方程:f[i][v] = max(f[i-1][v],f[i-1][v-c[i]]+w[i])

据观察当前状态只与二维数组前一行有关,定义f[v]为前i件物品放进容量为v的背包里所获得的最大价值。

转移方程: f[v]=max{f[v] , f[v-c[i]]+w[i]}

伪码如下:

for i=1..n
for j=v..0
f[j]=max{f[j],f[j-c[i]]+w[i]};

 

2、完全背包。

和0-1背包一样,只是现在每一样物品都有无限件可以选。

转移方程:f[i+1][j] = max{f[i][j-k*c[i]]+k*w[i]} 0<=k<=j/c[i]

写一个三重循环出来,效率显然很低。

在f[i+1][j]的计算中选择k个的情况与在f[i+1][j-c[i]]的计算中选择k-1个的情况是一样的,所以f[i+1][j]的递推中k>=1的部分的计算已经在f[i+1][j-c[i]]的计算中完成了。那么可以根据如下方式进行变换。

f[i+1][j] = max{f[i][j-k*c[i]]+k*v[i]   k>=0}

            =  max{f[i][j] ,    max{f[i][j-k*c[i]]+k*v[i]}    k>=1}

            = max{f[i][j]   ,  max{f[i][(j-c[i])-k*c[i]]+v[i]+k*v[i]}    k>=0}  (注意联系k的取值范围)

            = max {f[i][j],  f[i+1][j-c[i]]+v[i]}

代码

 1 public void helper()
 2 {
 3     for(int i=0;i<n;i++)
 4         for(int j=0;j<=W;j++)
 5     {
 6         if(j<w[i])
 7             dp[i+1][j] = dp[i][j];
 8         else
 9             dp[i+1][j] = Math.max(dp[i][j],dp[i+1][j-c[i]+v[i]);
10     }
11 }

一维优化为 

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}

伪码如下:

for i=1..n
for j=c[i]..v
f[v]=max{f[j],f[j-c[i]]+w[i]}

3、多重背包

对于第i件物品,个数为m[i]。

转移方程只需稍作修改

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=m[i]}

优化的方法:
运用二进制进行物品拆分,转化成01背包。
如:把13个相同的物品分成4组(1,2,4,6)
用这4组可以组成任意一个1~13之间的数
原理:一个数总可以用2^k表示
而且总和等于13,所以不会组成超过13的数

所以可将一种有C个的物品拆分成1,2,4,...,2^(k-1),C-(2^k-1)
然后进行01背包


 

#Edit : 2020.1.17

 

 

 

 

————2020.1.17————

标签:定义   拆分   void   else   san   表示   维数   mic   个数   

原文地址:https://www.cnblogs.com/zzl1209/p/12207528.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!