二进制优化多重背包
二进制优化的思想是把一种物品的多个,分解成二进制下的新物品。比如有一个物品有11个,重量为w[i],价值为v[i]。二进制下是1011,那么我们就把它分解成一个w[i],2w[i]和4w[i]和4*w[i]。这样拆分后,任何一个1-11的数均可以用这4个数表示,而复杂度却降成了log级别。
拆分完成后就与01背包相同了。
vector<int> w,v; //新物品的重量及价值
for (int i = 1; i <= n; i++) //拆分n个物品
{
for (int j = 1; num[i] >= j; j *= 2 ) //二进制拆解
{
w.push_back(wx[i]*j); //新的重量与价值
v.push_back(vx[i]*j);
num[i] -= j; //对应的总数减少
}
if( num[i] > 0 ) //把剩余的物品再打包一份成新的
{
w.push_back(num[i]*wx[i]);
v.push_back(num[i]*vx[i]);
}
}
单调队列优化多重背包
#include <iostream>
#include <queue>
using namespace std;
struct node{
int id,val;
node(int a = 0,int b = 0)
{
id = a;
val = b;
}
}q[100005]; //单调队列
int dp[100005];
int w[100005],v[100005],c[100005];
void slove()
{
int s,n;
cin >> s >> n;
int head,tail; //维护头指针和尾指针即可
for (int i = 0; i <= s; i++) dp[i] = 0;
for (int i = 1; i <= n; i++) //枚举物品
{
cin >> w[i] >> v[i] >> c[i];
if( w[i] > s ) continue; //重量超过容量
for (int j = 0; j < w[i]; j++) //枚举重量的余数
{
head = tail = 0;
for (int k = 0; k <= (s-j) / w[i]; k++) //枚举要取的数量
{
int id = k; //id表示当前处理的是dp[id*w[i]+j]
int val = dp[k*w[i]+j] - k * v[i]; //往单调队列中放置需要减去k*v[i],到要用的时候统一加
while( head < tail && val >= q[tail-1].val ) tail --; //维护单调递增队列
q[tail] = node(id,val);
tail ++;
while( q[head].id < k - c[i] ) head ++; //dp[k*w[i]+j]转移最远从dp[(k-c[i])*w[i]+j]转移过来
dp[k*w[i]+j] = q[head].val + k * v[i]; //要用时对答案的贡献均加上k*v[i]
}
}
}
cout << dp[s] << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while( t-- )
{
slove();
}
return 0;
}