下面就是多重背包的原题(借用洛谷的)
题目描述
终于,破解了千年的难题。小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;
}