【学习笔记】多重背包优化
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;
}
单调队列优化
对于转移方程:
当
v_i = 4 ,j 的值如下:1,2,3,4,5,6,7,8,9,10,11,12 \dots 可以发现
1 \equiv 4 (\text{mod } v_i),2 \equiv 5(\text{mod } v_i) ,以此类推。
因此,我们可以根据
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;
}
例题
P1782 旅行商的背包
P1833 樱花
P2760 科技庄园
P7381 [COCI2018-2019#6] Sličice