01背包
01背包问题
引题
采药(luogu P1048)
题解:
根据数据规模,可以用DFS+记忆化(记忆化搜索);
但这不是官方题解。这道题最简单的办法是01背包。
何为01背包?一道例题来说明;
(acwing 2)01背包问题
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
分析:
已知题意是:多种物品,每种物品只有一个.求能获得的最大总价值.
那么,问题在于怎么选才能获得的最大总价值
不考虑一切,本题可以用DFS做;
过程如下:
*0为不取,1为取;
每一件物品都只有2种状态:取或不取。
那么,dfs=ac。
呵呵o( ̄︶ ̄)o,你太天真了。
如果用DFS就能ac,这题的数据范围是吃****的么?
所以,请优化或改变策略。
记忆化搜索,不行。
DP,正解。
多种物品,每种物品只有一个.求能获得的最大总价值.
我们考虑是否选择第i件物品时,是需要考虑前i-1件物品对答案的贡献的.
分析
如果我们不选择第i件物品,那我们就相当于是用i-1件物品,填充了体积为v的背包所得到的最优解.而我们选择第i件物品的时候,我们要得到体积为v的背包,我们需要通过填充用i-1件物品填充得到的体积为v-c[i]的背包得到体积为v的背包.
//请保证理解了上文再往下看.
所以根据上面的分析,我们很容易设出该问题的二维状态
f[i][v] 代表用i件物品填充为体积为v的背包得到的最大价值.
从而很容易的写出状态转移方程
f[i][v]=max(f[i−1][v],f[i−1][v−c[i]]+w[i]);
显然的DP思想,但通常情况下,这类“每种物品都有一个价值w和体积c.,你现在有一个背包容积为V,你想用一些物品装背包使得物品总价值最大.”的问题我们称之为背包问题。
背包问题是DP中的一种重要问题。
上述问题属于背包问题中最简单的一种——01背包。
所谓01背包,就是:对于每一件物品都只有2种状态:取或不取。且每件物品有且只有一件(等价于只能取一次)。
注:各类复杂的背包问题总可以变换为简单的0-1背包问题进行求解。
(此法太过高深,本蒟蒻不会)
详细分析:
状态转移方程是如何得来的?
对于当前第 i 件物品,我们需要考虑其是否能让我们得到更优解.
显然,根据上面的话我们选择第i件物品的时候,我们要得到体积为v的背包,我们需要通过填充用i-1件物品填充得到的体积为v-c[i]的背包得到体积为v的背包.
我们需要考虑到 v−c[i] 的情况.
当不选当前第 i 件物品的时候,就对应了状态转移方程中的 f[i−1][v] ,
而选择的时候就对应了 f[i−1][v−c[i]]+w[i] .
Q:是不是在状态转移方程中一定会选择当前i物品?
A:不会
我们考虑一个问题.
如果一个体积为5的物品价值为10,而还有一个体积为3的物品价值为12,一个体积为2的物品价值为8.显然我们会选择后者.
这样我们的状态转移方程中就不一定会选择i物品。
其实最好地去理解背包问题的话,还是自己手动模拟一下这个过程,会加深理解。
代码写法↓
for(int i=1;i<=n;i++)//枚举 物品
for(int j=1;j<=V;j++)//枚举体积
//这个位置是可以正序枚举的.
//一维01背包必须倒序
if(j>=c[i])
f[i][j]=max(f[i-1][j],f[i-1][j-c[i]]+w[i]);//状态转移方程.
else f[i][j]=f[i-1][j].
//上面的if语句是判断当前容量的背包能否被较小体积的背包填充得到.
//显然 如果j-c[i]<0我们无法填充
//(谁家背包的体积负啊 (#`O′)
但是二维下的01背包我们还是无法满足,怎么办?(空间再限制一下)
考虑一维如何写!
仔细观察会发现,二维状态中,我们的状态每次都会传递给i(就是说我们的前几行会变得没用.)
这就给了我们写一维dp的机会啊
所以我们理所当然地设状态 f[i] 代表体积为i的时候所能得到的最大价值.
则,一维的状态转移方程是
f[j]=max(f[j],f[j−c[i]]+w[i]).
容易发现的是,我们的 f[i] 只会被i以前的状态影响.
如果我们顺序枚举,我们的 f[i] 可能被前面的状态影响.
所以我们考虑倒叙枚举,这样我们的 f[i] 不会被i以前的状态影响,而我们更新的话也不会影响其他位置的状态.
(可以手绘一下这个过程,应该不是很难理解.)
代码写法↓
for(int i=1;i<=n;i++)//枚举物品
for(int j=V;j>=c[i];j--)//枚举体积
f[j]=max(f[j],f[j-c[i]]+w[i]);//状态转移方程.
//应该不是很难理解.
小结
01背包问题是背包问题中最基础,也是最典型的问题.其状态转移方程也是基础,再次声明:各类复杂的背包问题总可以变换为简单的0-1背包问题进行求解。
请保证看懂之后再看其它问题.
补充:采药这题就是裸的01背包问题,套模板就行。
还有这不是一篇题解!!!