划分大理石(多重背包+二进制优化)
Description
有价值分别为1…6的大理石各a[1…6]块,现要将它们分成两部分,使得两部分价值之和相等,问是否可以实现。其中大理石的总数不超过20000。
Input
有多组数据!
所以可能有多行
如果有0 0 0 0 0 0表示输入文件结束
其余的行为6个整数
Output
有多少行可行数据就有几行输出
如果划分成功,输出Can,否则Can’t
Samples
Input 复制
4 7 4 5 9 1
9 8 1 7 2 4
6 6 8 5 9 2
1 6 6 1 0 7
5 9 3 8 8 4
0 0 0 0 0 0
Output
Can’t
Can
Can’t
Can’t
Can
思路:这个题就是多重背包。问是否能分成两等份,可以转化成sum/2是否存在,如果存在,那么剩余的肯定等于sum/2;
一般的多重背包:(朴素算法)
//n个物品,容量为m的背包,每个物品最多s[i]个,体积为v[i],价值为w[i],问
//包最大价值
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){
for(int k=0;k*v[i]<=j&&k<=s[i];k++){
f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
}
}
}
二进制优化
//n个物品,容量为m的背包,每个物品最多s[i]个,体积为v[i],价值为w[i],问
//包最大价值
//假设 50个苹果,取n个,若朴素算法是一个一个取,但二进制是把50个苹果先分成好几份(1,2,4,8,16,29(因为剩余的不满足2*16)这些相加==50)这些可以任意组成50以内的任何数,假设n是6,朴素算法要6次,二进制要2次(2,4)
int vv[maxn],ww[maxn],tot=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=s[i];j*=2){
vv[++tot]=v[i]*j;
ww[tot]=w[i]*j;
s[i]-=j;
}
if(s[i]){
vv[++tot]=v[i]*s[i];
ww[tot]=w[i]*s[i];
}
}
for(int i=1;i<=tot;i++){
for(int j=m;j>=vv[i];j--){
f[j]=max(f[j],f[j-vv[i]]+w[i]);
}
}
AC代码:
int a[7];
int w[maxn];
int f[maxn];
int main(){
while(1){
int sum=0,aa=0;
int num=0;
for(int i=1;i<=6;i++) {
cin>>a[i],sum+=i*a[i];
if(!a[i]) aa++;
for(int j=1;j<=a[i];j*=2){
w[++num]=i*j;
a[i]-=j;
}
if(a[i]) w[++num]=a[i]*i;
}
if(aa==6) return 0;
if(sum&1) {
puts("Can't");
continue;
}
memset(f,0,sizeof(f));
f[0]=1;
for(int i=1;i<=num;i++){
for(int j=sum/2;j>=w[i];j--){//这里吧体积换成了值,是否存在!!!妙
f[j]=max(f[j],f[j]+f[j-w[i]]);
}
}
if(f[sum/2]) cout<<"Can\n";
else puts("Can't");
}
return 0;
}