背包问题整理
来源:互联网 发布:js点击按钮切换div 编辑:程序博客网 时间:2024/06/16 15:08
准确一点说是对背包九讲的学习笔记 …
思想和方法:
在学习过程中了解的求解DP问题的思想,以及部分细节方法
1.DP初始化为一已知的合法解
2.滚动数组优化
3.”拆分物品”的思想和方法
4.当发现由熟悉的动态规划题目变形得来的题目时,在原来的状态中加一维以满足新的限制是一种比较通用的方法
01背包
问题:
有N件物品和一个容量为V的背包,第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
思路:
我们设dp[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值
策略为对每个物品选或不选
当前最优状态由在前i-1个物品的最优状态基础上选或不选当前物品决定
代码:
for(int i=1;i<=n;i++){ for(int j=v;j>=0;j--) //第二层for顺序倒叙皆可---因为二维存储状态,前i-1个物品的状态都已确定 { if(j>=c[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i]); else dp[i][j]=dp[i-1][j];//更新完全 }}for(int i=1;i<=n;i++)//附滚动数组优化 { for(int j=v;j>=c[i];j--)//枚举到c[i]即可,小于c[i]会产生负数下标 dp[j]=max(dp[j],dp[j-c[i]]+w[i]);}
完全背包
问题:
有N种物品和一个容量为V的背包,每种物品都有无限件可用,第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
思路:
我们设dp[i][j]表示前i种物品恰放入一个容量为j的背包可以获得的最大价值
策略为取0件、取1件、取2件…等很多种
则用自身的之前的最优状态和在前i-1种物品选取的最优状态基础上选取k个当前物品得到的状态取最优,得到状态转移方程如下:
dp[i][j]=max(dp[i][j],dp[i-1][j-c[i]*k]+w[i]*k); (0<=c[i]*k<=v)
时间复杂度O(n^3)
考虑优化:
首先有一简单有效的优化—若两件物品i,j满足c[i]<=c[j]且w[i]>=w[j],则将物品j去掉,不用考虑。
参考01背包的转移中,我们按照j=v..c[i]的逆序来循环。
这是因为要保证第i次循环中的状态dp[i][v]是由状态dp[i-1][v-c[i]]递推而来。
换句话说,这正是为了保证每件物品只选一次,保证在考虑”选入第i件物品”这件策略时,依据的是一个绝无已经选入第i件物品的子结果dp[i-1][j-c[i]]。
而现在完全背包的特点恰是每种物品可选无限件,所以在考虑”加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果dp[i][j-c[i]]。
所以就可以并且必须采用j=0..v的顺序循环,那么我们也就得到了状态转移方程如下
代码:
for(int i=1;i<=n;i++){ for(int j=0;j<=v;j++) { if(j>=c[i]) dp[i][j]=max(dp[i-1][j],dp[i][j-c[i]]+w[i]); else dp[i][j]=dp[i-1][j]; }}for(int i=1;i<=n;i++){ for(int j=c[i];j<=v;j++) dp[j]=max(d[j],dp[j-c[i]]+w[i]); }
还有一种证明思路 by http://blog.csdn.net/loi_lxt/article/details/77870376#t7
①dp[i][j]max=dp[i-1][j-k*w[i]]+k*v[i] 0<=k②dp[i][j]max=dp[i-1][j-w[i]-t*w[i]]+t*v[i]+v[i] 0<=t,1<=k dp[i-1][j] k==0③dp[i][j-w[i]]max=dp[i-1][j-w[i]-t*w[i]]+t*v[i] 0<=t将③带入②:dp[i][j]max = dp[i-1][j] dp[i][j-w[i]]+v[i]
多重背包
问题:
有N种物品和一个容量为V的背包。第i种物品最多有num[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
思路:
我们设dp[i][j]表示前i种物品恰放入一个容量为j的背包可以获得的最大价值,在这里与完全背包基本思路设置相同,得到状态转移方程如下:
dp[i][j]=max(dp[i][j],dp[i-1][j-c[i]*k]+w[i]*k); (0<=k<=num[i])
考虑优化:
我们考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略—取0..num[i]件,均能等价于取若干件代换以后的物品。
另外,取超过num[i]件的策略必不能出现。
将第i种物品共num[i]个划分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来单个的费用和价值乘以这个系数。
使这些系数分别为1,2,4,…,2^(k-1),num[i]-2^k+1,且k是满足num[i]-2^k+1>0的最大整数。
—对第i种物品整体二进制拆分,将第i种物品分成了log(n[i])种物品。
分成的这几件物品的系数和为num[i],表明不可能取多于num[i]件的第i种物品。
这种方法可以保证对于0…num[i]间的每一个整数,均可以用若干个系数的和表示,
那么对于拆分好的物品进行01背包的选择,就必定可以处理出对第i种物品的所有选择方案。
代码:
void ZeroOnePack(int c,int w){ for(int j=v;j>=c;j--) dp[j]=max(dp[j],dp[j-c]+w);}void CompletePack(int c,int w){ for(int j=c;j<=v;j++) dp[j]=max(dp[j],dp[j-c]+w);}void MultiplePack(int c,int w,int num){ if(c*num>=v)//数量足够可看作完全背包 { CompletePack(c,w); return; } int k=1; while(k<num) { ZeroOnePack(k*c,k*w); num-=k; k*=2; } ZeroOnePack(num*c,num*w);//num[i]-2^k+1的部分 }for(int i=1;i<=n;i++)MultiplePack(c[i],w[i],num[i]);
混合背包
问题:
就是混合三种背包啦~
思路:
分情况处理即可。
代码:
for(int i=1;i<=n;i++){ if(complete) CompletePack(c[i],w[i]); if(zeroone) ZeroOnePack(c[i],w[i]); if(multiple) MultiplePack(c[i],w[i],num[i]);}
二维费用背包
问题:
对于每件物品,具有两种不同的费用,选择这件物品必须同时付出这两种代价。
对于每种代价都有一个可付出的最大值(背包容量),问怎样选择物品可以得到最大的价值。
设第i件物品所需的两种代价分别为a[i]和b[i],两种代价可付出的最大值(两种背包容量)分别为v和u,物品的价值为w[i]。
思路:
加一维状态即可。
我们设dp[i][j][k]表示前i件物品付出两种代价为j和k时可获得的最大价值
dp[i][j]付出代价为i和j时的最大价值
状态转移方程如下:(以01背包为例)
代码:
for(int i=1;i<=n;i++){ for(int j=v;j>=a[i];j--) { for(int k=u;k>=b[i];k--) { if(j>=a[i]&&k>=b[i]) dp[i][j][k]=max(dp[i-1][j][k],f[i-1][j-a[i]][k-b[i]]+w[i]); else dp[i][j][k]=dp[i-1][j][k]; } }}for(int i=1;i<=n;i++){ for(int j=v;j>=a[i];j--) { for(int k=u;k>=b[i];k--) dp[j][k]=max(dp[j][k],dp[j-a[i]][k-b[i]]+w[i]); }}
最终答案为dp[n][v][u]。
完全背包顺序枚举,多重背包拆分处理即可。
特别的:
有时,”二维费用”的条件是以这样一种隐含的方式给出的:最多只能取m件物品。
这事实上相当于每件物品多了一种”件数”的费用,每个物品的件数费用均为1,可以付出的最大件数费用为M。
那么我们设dp[i][j]表示”付出费用i,最多选j件时可得到的最大价值”处理,其意义同滚动过的上文dp[i][j][k]。
注意:最后在dp[1..v][1..m]范围内寻找答案。
分组背包
问题:
有N件物品和一个容量为v的背包,第i件物品的费用是c[i],价值是w[i]。
这些物品被划分为若干m组,数量分别为siz[i],每组中的物品互相冲突,最多选一件。
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
思路:
我们设dp[k][j]表示前k组物品花费费用j能取得的最大价值
则对第i组物品策略为选择本组的某一件或是一件都不选,状态转移方程如下
代码:
for(int k=1;k<=m;i++){ for(int j=v;j>=0;j--) //必须在i层循环之外,保证每一组内的物品最多只有一个会被添加到背包中 { for(int i=1;i<=siz[k];i++)//i为第k组中的物品 { if(j>=c[i]) dp[k][j]=max(dp[k-1][j],dp[k-1][j-c[i]]+w[i]); else dp[k][j]=dp[k-1][j]; } } }for(int k=1;k<=m;i++){ for(int j=v;j>=0;j--) { for(int i=1;i<=siz[k];i++) { dp[j]=max(dp[j],dp[j-c[i]]+w[i]); } } }
也可以应用同完全背包一样的简单的优化:
对于每组内的物品,若两件物品i,j满足c[i]<=c[j]且w[i]>=w[j],则将物品j去掉,不用考虑。
有依赖的背包问题等其他背包问题暂且搁置。
东隅已逝,桑榆非晚..
- 背包问题整理
- 背包问题整理
- 背包问题整理
- 背包问题整理
- 01背包 问题 思路 整理
- 01背包问题代码整理
- 背包复习整理
- 背包整理模板
- 01背包整理
- 背包DP 整理
- 【无限背包】背包问题
- 背包问题---01背包
- 背包问题--部分背包
- 背包问题
- 背包问题
- 背包问题
- 背包问题
- 背包问题
- 支付宝sdk接入笔记
- 使用GCD处理几个线程之间的依赖关系。
- java内存分配浅析
- 进度报告——02
- 双十一临近,怎样让买家流畅地秒杀? ——腾讯WeTest独家开放电商产品压测服务
- 背包问题整理
- JS -- 函数组合
- ZOJ 3993 Safest Buildings (思路)
- 编译原理(七) 算符优先分析法(构造算符优先关系表算法及C++实现)
- 伪北漂....
- 学习C#以及C还有数据库
- SSE指令集优化学习:双线性插值
- 腾讯网移动端H5页面设计实战分享
- Leetcod:557.Reverse Words in a String III 反转字符串中的每个单词。