背包问题的DP过程
for 物品
for 体积
for 决策
三种背包共有代码
f[j] = max(f[j], f[j - v[i]] + w[i]);
01背包
每种物品只有一个
for (int i = 1;i <= n;i++)
for (int j = m;j>=v[i];j--)
f[j] = max(f[j], f[j - v[i]] + w[i]);
完全背包
每种物品有无限个
for (int i = 1;i <= n;i++)
for (int j = v[i];j <= m;j++)
f[j] = max(f[j], f[j - v[i]] + w[i]);
当空间优化成一维的时 只有完全背包的体积是从小到大循环的
多重背包
每种物品有有限个
朴素版本
时间复杂度
O(物品数量X最大体积X最大个数)
v w s 分别表示 体积 价值 数量
int v, w, s;
for (int i = 1;i <= n;i++)
{
cin >> v >> w >> s;
for (int j = m;j >= v;j--)
for (int k = 0;k <= s && k * v <= j;k++)
f[j] = max(f[j], f[j - k * v] + k * w);
}
二进制优化
时间复杂度
O(物品数量X最大体积Xlog最大个数)
a b s 分别表示 体积 价值 数量
将数量分装打包 转换成01背包
int cnt = 0;
for (int i = 1;i <= n;i++)
{
int a, b, s;
cin >> a >> b >> s;
int k = 1;
while (k <= s)
{
cnt++;
v[cnt] = a * k;
w[cnt] = b * k;
s -= k;
k *= 2;
}
if (s > 0)
{
cnt++;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
n = cnt;
多重背包变成01背包
体积是从大到小遍历的
for (int i = 1; i <= n; i ++ )
for (int j = m; j >= v[i]; j -- )
f[j] = max(f[j], f[j - v[i]] + w[i]);
分组背包
可仿照01背包优化到一维
需要逆向枚举体积
其中的”j>=v”意味着”j大于第i组中体积最小的物品体积值”
故可写成”j>=0”
s[i]表示第i组有多少个物品
for(int i=1;i<=n;i++)
for(int j=m;j>=0;j--)
for(int k=0;k<=s[i];k++)
if(j>=v[i][k])f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
多重背包是分组背包的特殊情况
混合背包
for (int i = 0;i < n;i++)
{
int v, w, s;
cin >> v >> w >> s;
if (s == 0)//完全背包 正序遍历体积
for (int j = v;j <= m;j++)f[j] = max(f[j], f[j - v] + w);
else//01背包也可以看成多重背包
{
if (s == -1)s = 1;//表示01背包 最多选1个
//多重背包二进制优化成01背包
for (int k = 1;k <= s;k *= 2)//k是基数也是权重
{
for (int j = m;j >= k * v;j--)
f[j] = max(f[j], f[j - k * v] + k * w);
s -= k;//从总数量中挖取2^k个(进行打包)
}
if (s)//对剩余的直接打包成一组 s是权重
for (int j = m;j >= s * v;j--)
f[j] = max(f[j], f[j - s * v] + s * w);
}
}
大佬tql
呜呜呜
%%%
呜呜呜
小朱🤤🤤🤤。。嘿嘿。。小朱🤤🤤
你是谁呀~
大佬orz
嘿嘿谢谢
右手就行
呜呜呜 左手不行