多重背包模版

下面就是多重背包的原题(借用洛谷的)

题目描述

终于,破解了千年的难题。小FF找到了王室的宝物室,里面堆满了无数价值连城的宝物……这下小FF可发财了,嘎嘎。但是这里的宝物实在是太多了,小FF的采集车似乎装不下那么多宝物。看来小FF只能含泪舍弃其中的一部分宝物了……小FF对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小FF有一个最大载重为W的采集车,洞穴里总共有n种宝物,每种宝物的价值为v[i],重量为w[i],每种宝物有m[i]件。小FF希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。

输入输出格式

输入格式:

第一行为一个整数N和w,分别表示宝物种数和采集车的最大载重。

接下来n行每行三个整数,其中第i行第一个数表示第i类品价值,第二个整数表示一件该类物品的重量,第三个整数为该类物品数量。

输出格式:

输出仅一个整数ans,表示在采集车不超载的情况下收集的宝物的最大价值。

输入输出样例

输入样例#1: 

4 20
3 9 3
5 9 1
9 4 2
8 1 3

输出样例#1: 

47

说明

对于30%的数据:n≤∑m[i]≤10^4;0≤W≤10^3。

对于100%的数据:n≤∑m[i]≤10^5;

0 <w≤4*10^4:1≤n<100。

这一道题又两种做法,一种是被倍增,另一种是单调队列

前者相信大家都会,我就不讲了,后者我看了很多博客,他们都写的我一脸懵逼

我研究了好久,终于知道怎么做了

首先定义一个dp数组

dp[i]表示到达i重量时的最大值

枚举1-n

对于当前的宝物,它的意义就是更新数组,所以我们至少要用到一个

举个栗子,假设当前的宝物重量为3,数量为3,价值不说(在这个栗子中没有意义)

用一个指针j从1一直到W,假设现在指到W-1,那么就是这样继承的

我们给这些格子分一个组,按照每w个为一组,这个继承的状态就十分的明显了

dp[j]=max(dp[j-k*w]+k*v)  (1<=k<=w)

因为分了组,每一组中相同位置的继承有几分相似,感觉就可以用单调队列了

可是因为公式k*v的阻碍,所以现在还不能用单调队列

所以我们可以考虑把公式改一改

假如当前的位置是r,要继承l

那么需要找出(r-l)有多少个v,我们本来想的是先把(r-l)算出,然后再算v

利用整式乘法,可以先找出r中v的个数,再减去l中v的个数

所以我们就可以巧妙的把r消去,从而进行单调队列的优化

代码就是先把所有位置除以w以后,余数相同的取出来一起算

我后面可能讲的不是很明白,但是你们看图就能明白是什么意思了

更加清晰详细的代码在后面

#include<cstdio>
#include<iostream>
using namespace std;
inline int read(){
    int x=0,f=0;char s=getchar();
    while(!isdigit(s))f|=s=='-',s=getchar();
    while( isdigit(s))x=(x<<1)+(x<<3)+s-48,s=getchar();
    return !f?x:-x;
}
inline void print(int x){
    if(x/10>0)print(x/10);
    putchar(x%10+'0');
}
const int N=4e4+10;
int n,W;
int ans,maxx,dp[N];
int head,tail;
struct node{
    int x,p;//x表示值,p表示位置 
}list[N];
int main(){
    int v,w,m;
    n=read();W=read();
    for(int i=1;i<=n;i++){   
        v=read();w=read();m=read();//读入 
        if(w==0){ans+=m*v;continue;}//如果w是0,就不用在算了 
        for(int j=0;j<w;j++){
            int t=(W-j)/w;//枚举所有的余数 
            head=1;tail=0;//一开始队列里面没有任何数 
            for(int k=0;k<=t;k++){//t表示W个格子可以分成的组数 
                int now=dp[j+k*w]-k*v;//将队列改为这种样子能够保证单调队列的正确性 
//这个地方要先入队列,否则队列里将会没有数,又因为以前用过所以可能导致计算结果出现错误 
                while(head<=tail&&now>=list[tail].x)tail--;//入队 
                list[++tail]=(node){now,k};
                while(head<=tail&&list[head].p<k-m)head++;//如果对头超过了这个范围,就head++ 
                dp[j+k*w]=max(dp[j+k*w],list[head].x+k*v);// 算出dp[j+k*w] 
                maxx=max(maxx,dp[j+k*w]);//记录最大值 
            } 
        } 
    }
    print(maxx+ans);//不要忘记加上ans哦 
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/zsyzClb/article/details/84582792