多重背包_单调队列优化

物品个数n,背包最大载重m,每个物品重w[i],价值v[i],最多c[i]个

当然c[i]=min(m/w[i],c[i])

--------------------------------------------------------------------------------------

传统多重背包:

for j=0 to m 
  for i=1 to n
    for k=0 to c[i] 
      f[i][j]=max(f[i-1][j-k*w[i]]+k*v[i])

k时间复杂度O(n*m*m/w[i]) 空间复杂度O(n*m)

--------------------------------------------------------------------------------------

观察转移方程:

*******************************这是前提:设b=j%w[i]  a=j/w[i]  a*w[i]+b=j  c=a-k*********************************   

f[i-1][j-k*w[i]]+k*v[i] --> f[i-1][b+c*w[i]]+(a-c)*v[i] --> f[i][j]=max( f[i-1][b+c*w[i]]-c*v[i] ) + a*w[i]

观察转化后的方程,可以通过max操作得到f[i][j]-a*w[i],当然a是可以通过j直接得到的

那么将循环改为

for i=1 to n
  for j=0 to m
    for k=0 to c[i]//c指的是输入数据中的c

当我们求f[i][j]时,f[i1][j1](i1<i,j1<j)想必已经被计算过

由于i处于第一层循环,且f[i]只与f[i-1]有关,滚动数组这一优化显然可以使用,这样就可以省去一维的空间复杂度

方程变为f[j]=max(f[b+c*w[i]]-c*w[i])+a*w[i]

---------------------------------------------------------------------------------------

单调队列优化目的:优化时间,基于上述,省去一维之后,我们考虑优化max操作

    for k=0 to c[i]//c指的是输入数据中的c

max是由k这层循环比较出来的,而max()中的f数组是i-1的时候计算出的,显然f是可以维护最大值的

f[b+c*w[i]]-c*w[i]丢进单调队列中,就可以解决问题,每次取出最大值+a*w[i]就可以得到要计算的新f值

----------------------------------------------------------------------------------------

单调队列规则:

队列的每个元素有两个值num和value,代表c和f值

队首为最大值,每次元素从队尾进队,若队中前一个本数<此元素则出队,此元素位置向前,如此循环

                            本元素⬇⬇
head<---*<---*<---*<---tail<---

同时当队首的num值不符合是要head+1

---------------------------------------------------------------------------------------

最后,为了代码实现方便,要将

for j=0 to m

这层循环稍改一下:

for(d=0;d<v[i];d++)  //由于变量冲突,d代表%的余数
  for(j=0;j<=(m-d)/v[i];j++)  

---------------------------------------------------------------------------------------

代码:大量变量名改变(说明时方便乱取变量名,现在后悔了)

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <set>
#define ll long long
using namespace std;
const int MAXN=7010;
int n,m;
int w[MAXN],v[MAXN],c[MAXN],dp[MAXN];
struct Q
{
	int index,value;
}q[MAXN];
inline int in()
{
	int x=0,flag=1;
	char ch=getchar();
	while (ch<'0'||ch>'9') {if (ch=='-') flag=-1;ch=getchar();}
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x*flag;
}
int main()
{
	n=in(),m=in();
	for (int i=1;i<=n;i++) w[i]=in(),v[i]=in(),c[i]=in();
	for (int i=1;i<=n;i++)
	{
		for (int d=0;d<w[i];d++)
		{
			int front=0,rear=-1;
			for (int j=d,r=0;j<=m;j+=w[i],r++)
			{
				int g=dp[j]-v[i]*r;
				if (front<=rear&&q[front].index<j-c[i]*w[i]) front++;
				while (front<=rear&&q[rear].value<=g) rear--;
				q[++rear]=(Q){j,g};
				dp[j]=q[front].value+r*v[i];
			}
		}
	}
	printf("%d",dp[m]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/wangyc123456/article/details/80262866