博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
01背包问题学习笔记
阅读量:4626 次
发布时间:2019-06-09

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

背包问题是DP动态规划算法中比较经典的一类模型,在NOIP考场上不定期地上位,令人琢磨不透,但是一旦学会了他,你就可以在短短十分钟的时间里,切掉他,达到节约时间,而且一次AC的目的.——某位大佬

01背包 完全背包 多重背包 分组背包 混合背包
对于物品而言只能选择1个或者0个两种情况 对于物品而言可以无限制选取,也可以不选 对于物品而言最多能够选择从s[i]个,同样也可不选 一些物品捆绑在一起,每一组物品中只能选择其中的一个物品 有些物品可以选择1,有些物品可以选择无数个,有些物品只能选择是s[i]个.即:01背包+完全背包+多重背包.
滚动数组 滚动数组 滚动数组 滚动数组 滚动数组
暂无 暂无 二进制优化或者单调队列优化 暂无 其中多重背包部分参考多重背包优化

以上就是本次的思维导图,即学习框架.

首先我们以01背包,所有背包的基础,开始进行本次的学习!

01背包

\(N\) 件物品和一个容量是 \(V\) 的背包。每件物品只能使用一次。

\(i\) 件物品的体积是 \(v_i\),价值是 \(w_i\)

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行两个整数,\(N,V\),用空格隔开,分别表示物品数量和背包容积。

接下来有 \(N\) 行,每行两个整数 \(v_i, w_i\),用空格隔开,分别表示第 \(i\) 件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

\(0 \lt N, V \le 1000\)
\(0\lt v_i, w_i \le 1000\)

输入样例

4 51 22 43 44 5

输出样例:

8
朴素思路

首先这道题目很明显的思路就是,对于一个物品而言,我们只有两种选择.

  1. 可以选择将他放入背包
  2. 不选择他,不放入背包中.
    那么一个朴素思路就是,我们可以使用二进制枚举,枚举每一个物品,我们到底是,选择这个物品,还是不选择这个物品.
    时间复杂度\(O(2^N)\)
DP思想 \(O(n \times m)\)

对于上面所说的复杂度,我们显然是不满意的,那么既然如此的话,我们如何去优化他呢?我们会想到动态规划(DP),因为这道题目有几个特殊的地方:

  1. 我们选择第i件物品,对于后面的物品并没有任何影响,即无后效.
  2. 它的决策非常明显,也就是我们只有,选择这个物品 或者 不选择这个物品这两种方案.

综上所述,我们可以先考虑考虑DP算法.

那么现在我们就要开始动态规划算法的思考题目流程.

  1. 状态数组如何定义
  2. 阶段是什么
  3. 决策是什么
  4. 边界是什么
  5. 状态转移方程
状态数组的定义

状态数组的定义:往往就是题目要我们求解的最终答案,所以我们大致可以这么定义状态数组.

对于\(f[i][j]\),它表示为,在前i个物品选取若干个,已经花费的容量为j的最大利润.
举个例子,\(f[3][5]\)表示为在前三个物品中,我们选取0或者1或者2或者3个,然后已经花费了5点容量的最大利润.
如果觉得例子解释不好,可以不看,害怕误解

阶段

根据我们的状态数组,我们可以这么选择阶段,也就是\(1 \le i \le n\),就是以物品为阶段.

决策

决策很明显,就是选取或者不选选取

边界条件

f数组全部清空就好了,因为我们要求的是最大利润.

状态转移方程
f[i][j]=max(f[i-1][j],f[i][j-v[i]]+w[i];

\(f[i-1][j]\)就是前i-1个物品,花费j个容量的最大利润,在这里我们可以理解为,不选取第i个物品(即选取前i-1个)的最大利润.

\(f[i][j-v[i]]+w[i]\)就是选取第i个物品,花费为j个容量的最大利润,那么为什么要这么写?
因为我们要放入这个物品,那么至少我们得需要\(v[i]\)个容量,所以我们只能从\(j-v[i]\)这个容量推过来,不然我们放不下这个背包.
\(+w[i]\)很明显就是选取了这个物品,当然要将这个物品的价值加上去.

滚动数组优化

第一步优化:

首先,对于状态数组而言,我们只利用了\(f[i][j]\)\(f[i-1][j]\).如下面这个例子所示

  1. 当i为1的时候,我们利用的是\(f[0][j]\)\(f[1][j]\)
  2. 当i为1的时候,我们利用的是\(f[1][j]\)\(f[2][j]\)
  3. 当i为1的时候,我们利用的是\(f[2][j]\)\(f[3][j]\)

所以其实我们只需要利用\(f[0][j]\)\(f[1][j]\)就好了,0实际上表示i-1,1实际上表示i.

第二步优化:
实际上我们的j不一定要从\(v[i],1+v[i],2+v[i]...m\),而是可以从\(m,m-1.m-2...v[i]\)因为这样的话,我们就可以从\(f[2][n]\)\(f[n]\)了.因为刚开始没有处理第二种决策的时候,\(f[1][j]\)肯定等于\(f[0][j]\),然后我们从m到\(v[i]\),开始从上到下开始决策,就可以忽略\(f[0]\)这一维了.
这里讲的不太好,建议还是自行模拟一遍吧= ̄ω ̄=

具体代码实现
#include 
using namespace std;const int N=1100;const int M=2e5;int n,m,i,j,k,a[N],f[M];int main(){ ios::sync_with_stdio(false); cin>>m>>n;//读入最大容量和物品个数 for(int i=1;i<=n;i++) cin>>a[i]; memset(f,0xcf,sizeof(f));//我这里是初始化为-INF,其实初始化为0就好了. f[0]=0; for(int i=1;i<=n;i++)//选取i个物品 for(int j=m;j>=a[i];j--)//容量设定 f[j]=max(f[j],f[j-a[i]]+a[i]);//转移 int ans=0; for(int i=0;i<=m;i++)//f[n][i]表示选取n个物品,消耗容量为i的最有价值. ans=max(ans,f[i]); cout<

转载于:https://www.cnblogs.com/gzh-red/p/11011573.html

你可能感兴趣的文章
分治——最近点对问题 hdu1007
查看>>
php值传参,引用传参以及&对象传参
查看>>
(转)iPhone开发经典语录集锦
查看>>
Linux常用命令
查看>>
Linux文件系统构成(第二版)
查看>>
杭电2099 整除的尾数
查看>>
Struts2--ActionContext及CleanUP Filter
查看>>
Spring MVC 学习笔记 对locale和theme的支持
查看>>
ntp时间同步服务
查看>>
Windows搭建wnmp
查看>>
请说明在.net中常用的几种页面间传递参数的方法,并说出他们的优缺点。
查看>>
12用户体验
查看>>
http://bbs.phome.net/showthread-13-45519-0.html
查看>>
POJ 1008 Maya Calendar / UVA 300【日期转换/常量数组】
查看>>
Java工具类-转换字符编码
查看>>
Pycharm中如何安装python库
查看>>
C++ transform for_each
查看>>
MySQL安装ODBC驱动出现126错误
查看>>
Redis持久化
查看>>
linux xampp eclipse xdebug 无法进入断点
查看>>