1.01背包
for(int i = 1;i <= n; i++)
for(int j = m; j >= w[i]; j--)//如果改成顺序f[i][j]就是由f[i][j-w[i]]推知,不符合题意
{f[j] = max(f[j] , f[j - w[i]]+v[i]);}
时间复杂度O(mn), 空间o(m)//优化了。也可以二维,f[i][j],二维可用滚动数组来优化(i只与i-1有关)
2.多重背包 规定每种物品固定的个数c[i]
for(int i = 1;i <= n; i++)
for(int j = m; j >= w[i]; j--)
for(int k = 1; k <=c[i] ; k++)
{ if( j - w[i]*k<0)break;
f[j] = max(f[j] , f[j - w[i]*k]+v[i]*k);}
时间复杂度O(mn∑c[i])
二进制优化
把 c[i] 分堆 ,分成1,2,4.......2^(K-1),s-2^k+1.// 10 分成1+2+4+3. k=3;
然后把这些堆每堆看成一件物品,用01背包做;时间复杂度O(mn∑log(c[i]))
int n1//堆的数量
for(int i = 1; i <= n; i++)
{
cin>>c1>>v1>>w1;//数量,价值,占背包的面积
int t = 1;
while(c1 >= t)
{
v[++n1] = t*v1;
w[n1] = t*w1;
c1 -= t;
t = t*2;
}
v[++n1] = v1*c1; w[n1] = w1*c1;//剩余部分单独分一堆 10 = 1+2+4+3,,这表示分3的那堆
}
for(int i = 1;i <= n1; i++)
for(int j = m; j >= w[i]; j--)
{f[j] = max(f[j] , f[j - w[i]]+v[i]);}
单调队列优化
元素从队尾进队,把比当前元素更劣的元素从队尾剔除,进队;
把不符合某种要求(在滑动窗口之外)的对首元素剔除;最后用对首 元素来更新f;
//维护一个单调下降的队列,手写队列.....
int head = 0 , tail = -1;
for(int i = 1; i <= n ;i ++)
{
while(head <= tail && q[tail].v <= v[i]) tail --;
q[++tail].v = v[i]; q[tail].num = i;
while(head <= tail && q[head].num + k < i)head ++;//k为移动框长度
f[i] = max(f[i],)
} //大概长这样因题而异
再来看关于多重背包的优化
对于 f[j] = max( g[j - w[i]*k]+v[i]*k)//0<=k <= min(c[i],m/w[i])(c[i]数量,w[i]占面积,v[i]价值)(g代表上一轮)
j mod w[i] 余数 为 r;
那么 j 只能被 mod w[i] 余数为r 的数给更新到;
即 r,r+w[i],r+2*w[i]........
设 j = n*w[i]+r
f[n*w[i]+r] = max(g[r +k* w[i] ]+(n-k)*v[i]) = max(f[r+ k*w[i]] - k*v[i])+n*v[i] ;
用单调队列维护f[r+ k*w[i]] - k*v[i]的最值就好