划分大理石(多重背包+二进制优化)

划分大理石(多重背包+二进制优化)

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;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值