对于有n种物品,每种mi件,背包容量为 v 的多重背包,如果直接转化成01背包,时间复杂度为∑mi*v,显然当两者乘积太大时,TLE无疑。现在有一种对用二进制减小∑mi的优化方法。
对于一种有mi件,体积为vi的商品,可以用 k+2种 体积分别为(1<<0)*vi,(1<<1)*vi . . . . . .(1<<k)*vi (k为满足 (1<<(k+1)) <=mi的最大取值) ,(mi - (1<<(k+1))+1)*vi,每种一件的商品来代替。
(1<<0),(1<<1) . . . . . .(1<<k)可以表示 1 到 (1<<(k+1))-1 内的所有数,然后再加上mi - (1<<(k+1))+1则可以表示 1 到 mi之间的所有数。
例如 mi = 13,则可分解成 1,2,4,和一个 6。
也就是说对于一种有mi件的商品可以分解成有(k+2)种只有一件的商品来代替。显然(k+2) <= mi恒成立,且mi越大这种优化效果越明显。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cmath>
#include <algorithm>
#include <stack>
#define LL long long
#define ULL unsigned long long
#define PI (acos(-1.0))
#define EPS (1e-10)
#pragma comment(linker,"/STACK:102400000,1024000")
using namespace std;
bool ans[120010];
int main()
{
//freopen("asdfas.txt","w",stdout);
int i,k,j,a[6],sum,t,s;
bool mark = true;
int icase = 1;
while(1)
{
for(sum = 0,i = 0,mark = false;i < 6; ++i)
{
scanf("%d",&a[i]);
if(a[i])
{
mark = true;
sum += a[i]*(i+1);
}
}
if(mark == false)
return 0;
printf("Collection #%d:\n",icase++);
if((sum&1) == 0)
{
sum >>= 1;
memset(ans,false,sizeof(bool)*(sum+3));
ans[0] = true;
for(i = 0;i < 6; ++i)
{
a[i] = min(a[i],sum/(i+1));
}
for(i = 0;i < 6; ++i)
{
if(a[i])
{
for(j = (a[i]>>1),t = 1;t <= j;t <<= 1)
{
s = t*(i+1);
for(k = sum;k >= s; --k)
{
if(ans[k-s])
ans[k] = true;
}
}
for(k = sum,j = (a[i]-t+1)*(i+1);k >= j; --k)
{
if(ans[k-j])
ans[k] = true;
}
}
}
if(ans[sum])
printf("Can be divided.\n\n");
else
printf("Can't be divided.\n\n");
}
else
{
printf("Can't be divided.\n\n");
}
}
return 0;
}