多重背包应该算是背包问题里面比较麻烦的一种了,基本思路就是拆分转化为01背包,进而套01背包的模版
今天在洛谷刷题的时候遇到一道题,是一个多重背包和完全背包的组合型问题
题目描述
爱与愁大神后院里种了n棵樱花树,每棵都有美学值Ci。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看Ai遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间Ti。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。
输入
共n+1行:
第1行:三个数:现在时间Ts(几点:几分),去上学的时间Te(几点:几分),爱与愁大神院子里有几棵樱花树n。
第2行~第n+1行:每行三个数:看完第i棵树的耗费时间Ti,第i棵树的美学值Ci,看第i棵树的次数Pi(Pi=0表示无数次,Pi是其他数字表示最多可看的次数Pi)。
输出
只有一个整数,表示最大美学值。
样例输入
6:50 7:00 3
2 1 0
3 3 1
4 5 4
样例输出
11
这道题我一开始的思路是用时间的限制求出完全背包的上限,然后将完全背包转换为多重背包,进而进行拆分,就可以套01背包的板子
这是一个我作为一名蒟弱 的的第一反应
交完之后很尴尬的,除了第一个点都TLE了,后来发现这道题在拆分的时候要用到2进制拆分
二进制拆分:
方法:
将一个数字用根据2的多少次方进行拆分,如:
7=1+2+4;
9=1+2+4+2;
这样做的好处就是减少了占用的空间,并且将拆分数字组合相加可以得到(1~n)之间的任何一个数字,并且组合方式较少
而如果采用拆分成1的话,就会产生较多的组合方式,造成了重复运算
最后,上面那道题的代码
#include <iostream>
#include <stdio.h>
using namespace std;
int sumt,num,x,y,z,T[100005],M[100005],ans[10001];
int main(){
int h1,h2,m1,m2,n;
scanf("%d:%d %d:%d %d",&h1,&m1,&h2,&m2,&n);
sumt=h2*60+m2-h1*60-m1;
for (int i = 0; i < n; ++i)
{
cin>>x>>y>>z;
if(z==0)
z=sumt/x+1;
int t=1;
while(z){
T[num]=t*x;
M[num]=t*y;
z-=t;
t*=2;
num++;
if(z<=t){
T[num]=z*x;
M[num]=z*y;
num++;
break;
}
}
}
for (int i = 0; i <= num; ++i)
{
for (int j = sumt; j >= T[i]; --j)
{
ans[j]=max(ans[j],ans[j-T[i]]+M[i]);
}
}
cout<<ans[sumt]<<endl;
}