你有一个容量为M的背包,和N种物品。
每种物品都有三个属性,vi,wi,与ci,分别表示这种物品的体积、价值和件数。
你的任务是,从这些所给物品中,选出若干件,其体积之和不能超过背包容量,并且使所选物品的权值的和最大。
朴素的方法当然是先枚举物品再枚举体积最后再枚举个数,但是这样的时间复杂度太高,接受不了,所以可以考虑用单调队列优化DP。发现单调队列维护的区间一定是一段连续的区间,但是由于v[i]的限制,如果还按照原来的顺序枚举,这样我们的决策点将不满足连续,而是离散的,所以我们要想办法解决这个问题才可以用单调队列优化。发现在模v[i]的同一个剩余类下的所有体积的决策点都是连续的(虽然看起来是离散,但是是连续的离散),可以用单调队列维护,所以我们先按照剩余类分类,对于每一个剩余类用一个单调队列维护,这样时间复杂度就只有 O(nm) O ( n m ) 了。
/*========================
* Author : ylsoi
* Problem : bag
* Algorithm : DP
* Time : 2018.5.8
* =====================*/
#include
#include
#include
#include
#include
#include
#include
using namespace std;
void File(){
freopen("[codevs5429].in","r",stdin);
freopen("[codevs5429].out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
#define PII pair
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
const int maxn=7000+10;
int n,m,v[maxn],w[maxn],c[maxn],dp[maxn],tmp[maxn];
deque< PII >qu;
void Push(int x,int pos){
while(qu.size() && qu.back().first<=x)qu.pop_back();
qu.push_back((PII){x,pos});
}
void Pop(int pos){
while(qu.size() && qu.front().secondint main(){
File();
scanf("%d%d",&n,&m);
REP(i,1,n)scanf("%d%d%d",&v[i],&w[i],&c[i]);
REP(i,1,n){
mem(tmp);
REP(k,0,v[i]-1){
qu.clear();
for(register int l=0;l*v[i]+k<=m;++l){
int j=l*v[i]+k;
Push(dp[j]-l*w[i],l);
Pop(l-c[i]);
chkmax(tmp[j],qu.front().first+l*w[i]);
}
}
REP(j,0,m)chkmax(dp[j],tmp[j]);
}
printf("%d\n",dp[m]);
return 0;
}