博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
背包九讲
阅读量:5163 次
发布时间:2019-06-13

本文共 2973 字,大约阅读时间需要 9 分钟。

0-1背包

有n个物品,每个物品都有两个属性:价值v和重量w。有一个容量为W的背包,现要求这些物品尽可能装进背包,并使得价值最大。

因为每种物品只有一个,选或者不选,因此也叫做0-1背包

记Res(i,j)为还有编号为1~i的物品可以选,背包剩余容量为 j 时所能获得的最大价值

很明显 Res(0,j) =0 ,因为还有0个物品可以选,将这0个物品放入还剩 j 容量的背包所能获得的价值当然为0

当还有1~i个物品可以选,背包容量为 j 时,对于编号为 i 的物品,只有两种选择:选或者不选

1、不选编号为 i 的物品,那么Res(i, j) = Res(i-1, j), 下一步只考虑前 i-1个物品,且背包容量不变

2、选编号为 i 的物品, 那么Res(i, j) = Res(i-1, j-w[i]) + v[i] ,下一步只考虑前 i-1个物品,因为已经选了编号为 i 的物品,则背包容量相应的减小w[i] ,总价值增加 v[i]

还有考虑一些细节:

1、边界条件:Res(0,j) = 0

2、j<w[i] , 背包所剩容量不足以放入编号为 i 的物品,Res(i, j) = Res(i-1, j)

所以可以得到方程:

                      0                                                           i = 0

Res(i ,j) =    Res(i-1,j)                                                 j<w[i]

                   max{Res(i-1, j) , Res(i-1, j-w[i])+v[i] }      other

递归程序:

 

#include
#include
using namespace std;int w[5] = {
0,2,1,3,2};int v[5] = {
0,3,2,4,2};int n = 4, W = 5;int Res(int i, int j){ if (i == 0) //还剩0个物品 return 0; else if (j < w[i]){ //剩余容量不足,不能选 return Res(i-1,j); } else{ return max(Res(i-1,j),Res(i-1,j-w[i])+v[i]); }}int main(){ cout << Res(4,5) << endl; return 0;}
View Code

 

 如果利用dp求解,将上述方程Res(i,j) 变成 dp[i][j]

dp程序:

 

#include
#include
#include
using namespace std;int w[5] = {
0,2,1,3,2};int v[5] = {
0,3,2,4,2};int n = 4, W = 5;int dp[10][10];int main(){ memset(dp,0,sizeof dp); for (int i = 1;i<=n; i++){ //从1开始,因为已经确定i=0时,dp=0 for (int j = 0; j <= W; j++){ if (j < w[i]) //背包容量不足 dp[i][j] = dp[i - 1][j]; else dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]); } } cout << dp[n][W] << endl; return 0;}
View Code

 

空间优化:

从上面的方程可以看出,不管是选还是没被选,第i行的数据来自第i-1行,所以没必要开二维数组存表,只需要开两行不断更新就好了,因为算第i+1行的答案用不到第i-1行的数据

代码:

#include
#include
#include
using namespace std;int w[5] = {
0,2,1,3,2};int v[5] = {
0,3,2,4,2};int n = 4, W = 5;int dp[10];int main(){ memset(dp,0,sizeof dp); for (int i = 1;i<=n; i++){ //从1开始,因为已经确定i=0时,dp=0 for (int j = W; j>= 0; j--){ //容量递减 if (j < w[i]) //背包容量不足 dp[j] = dp[j]; else dp[j] = max(dp[j],dp[j-w[i]]+v[i]); } } cout << dp[W] << endl; //注意输出 return 0;}
View Code

 

再来看看初始化的细节:

背包问题一般有两种:

1、要求背包必须填满时的最优解

2、不要求背包必须填满,只要价值尽可能大就ok

我们上面讨论的就是第2种。

如果是第一种,则初始化dp[0]=0,dp[1~W] 为-∞


完全背包

 对于每个物品,可以选任意个

 
#include
#include
#include
using namespace std;int n=3,W=7;int v[] = {
0,4,5,3};int w[] = {
0,3,4,2};int dp[10];int main(){ memset(dp, 0, sizeof dp); for (int i = 1; i <= n; i++){ for (int j = 0; j <= W; j++){ //这里时升序,和0-1背包相反 if (j < w[i]) dp[j] = dp[j]; else dp[j] = max(dp[j],dp[j - w[i]] + v[i]); } } cout << dp[W] << endl; return 0;}
View Code

 

 
 

 

转载于:https://www.cnblogs.com/looeyWei/p/10467597.html

你可能感兴趣的文章
ORA-12560: TNS: 协议适配器错误 ORA-12154: TNS: 无法解析指定的连接标识符
查看>>
读书印记 - 《菊与刀》
查看>>
第一个小demo: spring boot + mybatis + thymeleaf
查看>>
mysql获取字段信息
查看>>
Tomcat 网站部署(三)
查看>>
JS实现全选与取消 Jquery判断checkbox是否被选中
查看>>
如果重新设计网络,有没有可能合并IP地址跟MAC地址?
查看>>
德州扑克总纲
查看>>
linux下password的用法
查看>>
[poj1986]Distance Queries(LCA)
查看>>
BPM配置故事之案例11-操作外部数据源
查看>>
uniGUI试用笔记(一)
查看>>
漫谈python中的搜索/排序
查看>>
js_类数组转化为数组
查看>>
centos 7 安装 rvm 超时
查看>>
类库间无项目引用时,在编译时拷贝DLL
查看>>
module 'socket' has no attribute的解决方案
查看>>
Java NIO vs. IO
查看>>
BIO、NIO、AIO通信机制
查看>>
STL priority_queue<> 用法 <转>
查看>>