之前有过一篇多重背包判断是否能凑一个整数的问题->传送门
如果我们不是判断是否能凑成这个数,而是计算这个数内的背包最大值,就不能用这个写法了(也可能是我太菜了,一直WA)
于是就有了二进制背包的方式
在多重背包中如果每个背包被选择的次数maxi都比较小
我们可以把他拆违maxi个相同的背包
但如果maxi可能比较大,那么最后的背包总数就可能会超时
怎么把这个maxi用一些更小的数字来组合而成呢?
我们都知道,任意一个正整数都能被表示为二进制之和,而2的0次方即1可以表示任意一个数,所以如果在多重背包问题中我们知道每个背包能选择的最大次数maxi,我们就可以把maxi分解为二进制,把其中某些二进制数加起来就能得到小于等于maxi的任意一个数。
如果没有理解,我们就看这个例子
example:
我们有个背包最多能被选择40次,也就是我们可以选择0到40次这个背包,而40=1+2+4+8+16+9,1、2、4、8、16、9这几个数就可以构成1到40中的任意一个数,我们如果不选择这个背包就是0。(!!!注意:一定是从2的0次方开始递增分解)
P1833
#include <bits/stdc++.h>
using namespace std;
int ans[1005];//all可能会很多,所以不能用二维的背包
int t[10005];
int score[10005];
int c[10005];
int a[1000005];
int b[1000005];
int all=0;
int n;
void transf()//转化背包
{
for(int i=1;i<=n;i++)
{
int tr=1;
while(c[i])
{
a[++all]=tr*t[i];
b[all]=tr*score[i];
c[i]-=tr;
tr*=2;
if(c[i]<=tr)
{
a[++all]=c[i]*t[i];
b[all]=c[i]*score[i];
break;
}
}
}
}
int main()
{
int h1,m1,h2,m2;
scanf("%d %*c%d%d%*c%d",&h1,&m1,&h2,&m2);
cin >> n;
for(int i=1;i<=n;i++)
{
cin >> t[i] >> score[i] >> c[i];
if(!c[i])
c[i]=100000;
}
transf();
if(h2<h1)
h2+=24;
int T=(h2*60+m2)-(h1*60+m1);
for(int i=1;i<=all;i++)
{
for(int j=T;j>=a[i];j--)
{
ans[j]=max(ans[j],ans[j-a[i]]+b[i]);
}
}
cout << ans[T] << endl ;
return 0;
}