背包¶
# 套路 & 经验积累 DP - chenhongxuan的小破站 - 洛谷博客
01背包¶
塞满证明:01背包装满问题(dp)_01 物品是否能将背包装满呢-CSDN博客
关于初始化的一些小技巧:
使用一维数组:
- 如果题目没有要求恰好把背包装满,那么很简单,dp数组全部初始化为0即可。
- 如果题目要求恰好把背包装满,且求的的是价值的最大值,那么dp数组全部初始化为-INF,仅仅把dp[0]置为0。
- 如果题目要求恰好把背包装满,但是求的是价值的最小值,那么dp数组全部初始化为INF,仅仅把dp[0]置为0。
- 恰好塞满求方案,则只将dp[0]置为1。
多重背包——二进制分组优化¶
原理¶
如果p=1000
朴素的算法需要遍历1000多次
等价转换:我们知道二进制可以表示出所有正整数
将数量拆解,从1开始,尽可能多的二进制数
这样就能用这些新数字重新表示1~1000的所有数字
而不必一个一个遍历
但不等同于类似于快速幂的迭代求解
这类问题是分解原数字
即拆成尽可能少的二进制数字来简便运算
实现¶
倍增k 将1000个依次递减 比如:
1000 = 1、2、4、8、16、32、64、128、256、489
int k=1;
while(k<p[i]){
// for in capa:: DP k;
p[i]-=k;
k<<=1;
}
// 偷懒:直接copy上面的dp方程即可
k=p[i];
// for in capa:: DP k(489);
(直接在dp时进行)
int num[maxn][2], dp[maxn];
int N, V, c, w, n, t;
cin >> V >> N;
for(int i = 1; i <= N; ++i)
{
cin >> c >> w >> n;
for(int k = 1; k < n; k<<=1)
{
for(int j = V; j >= k*c; --j)
dp[j] = max(dp[j], dp[j-k*c]+k*w);
// 该“物品”的花费:k*cost[i] 收益:k*value[i]
n -= k;
}
for(int j = V; j >= n*c; --j)
dp[j] = max(dp[j], dp[j-n*c]+n*w);
}
多重背包——单调队列优化¶
【动态规划/背包问题】多重背包の单调队列优化 - 知乎 (zhihu.com)
分组背包(泛化背包)¶
P7381 [COCI2018-2019#6] Sličice - 洛谷 | 计算机科学教育新生态
一个物品的价值随着你分配给它的费用而变化;约等于一个组只能选其中一个物品(为什么不是等价?想想看)一个组内可能有费用相同而价值不同的物品
先枚举组(物品),再枚举容量,最后是组内物品(或:物品在不同费用下的价值)。这样就确保了每个容量仅通过了一个组内的物品进行转移,因此还需要逆序(用0-1的代码)。
类比一下其他的背包:先枚举物品,再枚举容量,第三维组内只有一个物品。
也就是说设 \(f[k][v]\) 表示前k组物品花费费用v能取得的最大价值,则有:
\[
f[k][v]=max(f[k-1][v], f[k-1][v-c[i]]+w[i]) \qquad i \in k
\]
for k in 所有的组
for v=V..0
for i in 组k
f[v]=max{f[v],f[v-c[i]]+w[i];