【学习笔记】多重背包优化
basic tips
多重背包可以看做 背包和完全背包的结合。
例题:P1776 宝物筛选
这道题完全就是多重背包板子,多重背包就是在 背包与完全背包两者间取了个折中,对于每个体积 ,价值 的物品多了一个限制,每种物品有且仅有 个。
朴素做法
在背包不能装下当前枚举的第 个物品的所有 个时,直接转换成完全背包对当前物品进行求解。反之,则转换成 背包,枚举第 个物品的个数。
这样的复杂度即为 ,一旦数据稍微大一点就无法接受。
二进制优化
基于倍增思想,举个例子,当我们想拿 个物品时,我们会去枚举 到 ,但是若我们 这样去拿,最终结果仍然与朴素做法一样,但是仅仅 次就可以得出答案,所以我们是把 拆成 的形式。
所以背包在当前第 个装得下时,我们在枚举个数时直接每次拿 个物品去跑 背包,这样的复杂度 ,可以接受。
code
#include<bits/stdc++.h>
// #define int long long
using namespace std;
const int MAXN = 1e5 + 10;
int n,W,v[MAXN],w[MAXN],m[MAXN],dp[MAXN];
signed main()
{
scanf("%d %d",&n,&W);
int ind = 0;
for(int i = 1;i <= n;i ++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
int ret = 1;
while(ret <= c)
{
v[++ ind] = ret * a;
w[ind] = ret * b;
c -= ret;
ret <<= 1;
}
if(c > 0)
{
v[++ ind] = c * a;
w[ind] = c * b;
}
}
for(int i = 1;i <= ind;i ++)
{
for(int j = W;j >= w[i];j --)
dp[j] = max(dp[j],dp[j - w[i]] + v[i]);
}
printf("%d\n",dp[W]);
return 0;
}
单调队列优化
对于转移方程:,所以 的值定会被 所影响,举例:
当 , 的值如下:
可以发现 ,以此类推。
因此,我们可以根据 对 的模数对物品进行分类,所以可以得到 ,其中 为 对 的模数,移项得 ,修改转移方程得:,其中 ,就可以把 里那一坨用单调队列优化,复杂度 。
code
#include<bits/stdc++.h>
// #define int long long
using namespace std;
int n,W,v,w,c,q1[100005],q2[100005],head,tail,dp[100005];
signed main()
{
scanf("%d %d",&n,&W);
for(int i = 1;i <= n;i ++)
{
scanf("%d %d %d",&v,&w,&c);
int tmp = min(c,W / w);
for(int j = 0;j < w;j ++)
{
head = 1,tail = 0;
int k = (W - j) / w;
for(int t = 0;t <= k;t ++)
{
while(head <= tail && dp[j + t * w] - t * v > q2[tail]) tail --;
q1[++ tail] = t;
q2[tail] = dp[j + t * w] - t * v;
while(q1[head] < t - tmp) head ++;
dp[j + t * w] = max(dp[j + t * w],q2[head] + t * v);
}
}
}
printf("%d\n",dp[W]);
return 0;
}
例题
本文作者:_Tomori
本文链接:https://www.cnblogs.com/Tomori0505/p/18623003
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步