解题思路:
起初以为多重背包枚举状态会超时,但借鉴了别人的剪枝后就过了,
将价值从大到小枚举,那对于两个价值a1,a2(a1小于a2),当枚举同一级别的弹珠k时,若(a2-a1)%k==0,那a1+x个k可能会等于a2+y个k,而此后a1+z个k(z>x)就一定会等于a2+(y+(z-x))个k。若倒着枚举就可以break剪枝了。
详见代码。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=120005;
int T,n,sum,tot,num[6];
int f[N];
int main()
{
//freopen("lx.in","r",stdin);
int i,j,k;
while(1)
{
T++;sum=0;
for(i=1;i<=6;i++)
num[i]=getint(),sum+=num[i]*i;
if(!sum)break;
printf("Collection #%d:\n",T);
if(sum%2!=0)
{
puts("Can't be divided.");
putchar('\n');
continue;
}
memset(f,false,sizeof(f));
sum/=2,tot=0,f[0]=true;
for(i=1;i<=6;i++)
{
if(!num[i])continue;
for(j=tot;j>=0;j--)
if(f[j])
{
for(k=1;k<=num[i]&&j+k*i<=sum;k++)
{
if(f[j+k*i])break;
f[j+k*i]=true;
}
}
tot+=num[i]*i;
}
f[sum]?puts("Can be divided."):puts("Can't be divided.");
putchar('\n');
}
}