学习目标:
我太急切的想要一个答案。想要风光的学位,瞬间的博学,想要意气风发,想闪着金光走向喜欢的人。但现实告诉我,操之过急便会败北,他要我等,要我耐得住不断延长的时间线,要我交付出足够的努力堆砌在沉闷、晦涩的时光里,才肯将一切"我想要"一点一点递送至我手里。每日一题,一起逐梦。
学习内容:
我们今天的内容是背包问题的进阶版——多重背包。
先来了解一下什么是多重背包:
与01背包、完全背包相比,多重背包的不同就在于物品的数目是有限的, 在有限的物品中挑选出最优的方案,是不是和01背包有些相似,也就是说,我们可不可以把n个相同的物品看作是分散的n个物品,他们只是体积和价值都相同。有些化整为零的思想,这样转化以后,我们是不是就能当作01背包的问题来做,没错,看下代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1005;
int f[N];
int main() {
int n, m;
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) {
int v,w,s;
scanf("%d%d%d",&v,&w,&s);
for(int k=0; k<s; k++) { //k个相同的物品分成k个体积和价值都相同的物品
for(int j = m; j >= v; j--) {
f[j] = max(f[j], f[j - v] + w);
}
}
}
printf("%d",f[m]);
return 0;
}
当然,这是再数据量较少的时候,及时我们优化了空间复杂度,它的时间复杂度依旧很高,当数据量增加的时候,我们还是会超时,这就需要我们来思考一下有没有什么优化的方法。思考一下
我们的时间复杂度主要是因为k的重复,很多个相同的物品我们要重复去算,而最后只要知道k个物品需要几个,我们除了一个一个的去算,还有没有更好的方法,减少k的循环。想想我们的二进制,可以把一个很大的数转化成32位以内,是不是可以再减少循环的同时保护我们最终的结果不变呢?
我们一开始是把每一个物品分成一堆,这时,我们可以把物品分成1、2、4、8、16...2^n,这样就能大大减少我们的运算时间,而且每个数,都可以用二进制来表示,我们先对物品进行分堆,然后再用01背包的方法去解决,就能将时间复杂度讲到O(n*m)。代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1000000;
int f[N];
struct good {
int w;
int val;
};
int main() {
int n, m;
vector<good> a;
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) {
int v,w,s;
scanf("%d%d%d",&v,&w,&s);
for(int k=1; k<=s; k=k*2) {
s-=k;//注意s需要不断减少
a.push_back({k*v,k*w});
}
if(s>0) {//最后s只能是0或1,因为二进制的原因。
a.push_back({s*v,s*w});
}
}
n=a.size();
for(int i=0;i<n;i++){
for(int j = m; j >= a[i].w; j--) {
f[j] = max(f[j], f[j - a[i].w] + a[i].val);
}
}
printf("%d",f[m]);
return 0;
}
有问题的朋友欢迎评论区留言讨论,小编近几天会陆续更新背包问题的文章,希望大家多多鼓励,共同进步!
每天坚持是一件很累的事,即使很慢,但也不要停止。
大家记得点赞收藏,防止找不到哦。
欢迎大家订阅小编的每日一题专栏,会每天更新,都是用心准备的哦!