多重背包

注意考虑下边界和0的大小关系

\[f[i][u+p*v_i]=\max_{max(0,p-c_i) \le k \le p-1 } \{ f[i-1][u+k*v_i]+(p-k)\times w_i \} \]

边界分析:
所求范围:
\(i\in [1,n],p \in [0,(m-u)/v_i],u\in [0,v_i)\)
递推边界:
\(p \in [1,(m-u)/v_i]\)
所以:
\(p=0\)\(f[i][u]=f[i-1][u]\)
i=1时:
\(u!=0,f=-INF\)
\(u=0\) 在max里只需要有一个为0即可 所以f[0]=0

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

const int N=1010;
const int M=20010;

int read()
{
	int x=0,f=0,c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return f?-x:x;
}

int c[N],v[N],w[N],f[M],n,m;
deque<int> q;

int calc(int i,int u,int k){return f[u+k*v[i]]-k*w[i];}

int main()
{
	n=read(); m=read();
	for(int i=1;i<=n;i++)
		v[i]=read(),w[i]=read(),c[i]=read();
	memset(f,0xcf,sizeof f); f[0]=0;
//要注意下边界和0的大小的判断	
	for(int i=1;i<=n;i++)
	{
		for(int u=0;u<v[i];u++)
		{
			while(q.size()) q.pop_back();
			int maxp=(m-u)/v[i];
			for(int k=maxp-1;k>=max(maxp-c[i]+1,0);k--)
			{
				while(q.size()&& calc(i,u,k) >= calc(i,u,q.back() ) ) q.pop_back();
				q.push_back(k);
			}
			for(int p=maxp;p>=1;p--)
			{
				while(q.size()&&q.front()>p-1) q.pop_front();
//注意取等号 
				if(p-c[i]>=0)
				{ 
					while(q.size()&&calc(i,u,q.back())<=calc(i,u,p-c[i]) ) q.pop_back();
					q.push_back(p-c[i]);
				} 
//通常来说 只要保证决策集合存在 就不需要判断队列是不是空 
				f[u+p*v[i]]=max(f[u+p*v[i]],calc(i,u,q.front())+p*w[i]);
			}
			
		}
	}	
	int ans=0;
	for(int i=1;i<=m;i++) ans=max(ans,f[i]);
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-02-27 12:12  __iostream  阅读(26)  评论(0)    收藏  举报