poj1014 - Dividing

133 篇文章 0 订阅

                                    想看更多的解题报告: http://blog.csdn.net/wangjian8006/article/details/7870410
                                     转载请注明出处:
http://blog.csdn.net/wangjian8006

题目大意:

有六个大理石,他们的价值分别是1,2,3,4,5,6,然后分别给出六个大理石的个数,问如何平分给两个人,令两个人所得到的价值相等。

 

前面想到了DFS,写了一个裸的DFS,结果TLE,结果不断剪枝,还是TLE,后来觉得已经无法再剪枝了,但是却WE了,后来看了一位大神的,他的和我的只有一个地方不同,以下两段代码,我改了一个地方就A了,但是其实质上没什么区别,所以我认为这题用DFS还是不能过的。应该用DP是对的。

这是A了的:

#include <iostream>
using namespace std;

int a[7],sum;

int dfs(int cap,int value){
	int i,temp;
	
	if(value==sum) return 1;

	for(i=cap;i>=1;i--)
		if(a[i]){
			temp=value+i;
			if(temp<=sum){
				a[i]--;
				if(dfs(i,temp)) return 1;
			}
		}
	return 0;
}

int main(){
	int flag,i,t;
	for(t=1;;t++){
		for(flag=sum=0,i=1;i<=6;i++){
			scanf("%d",&a[i]);
			sum+=i*a[i];
			if(!flag && a[i]) flag=1;
		}
		
		if(!flag) break;
		
		printf("Collection #%d:\n",t);

		if(sum&1){
			printf("Can't be divided.\n\n");
			continue;
		}
		sum=sum>>1;
		if(dfs(6,0))
			printf("Can be divided.\n\n");
		else
			printf("Can't be divided.\n\n");
	}
	return 0;
}

这是WE的:

 

#include <iostream>
using namespace std;

int a[7],sum;

int dfs(int cap,int value){
	int i,temp;
	
	if(value==sum) return 1;

	for(i=cap;i<=6;i++)
		if(a[i]){
			temp=value+i;
			if(temp<=sum){
				a[i]--;
				if(dfs(i,temp)) return 1;
			}
		}
	return 0;
}

int main(){
	int flag,i,t;
	for(t=1;;t++){
		for(flag=sum=0,i=1;i<=6;i++){
			scanf("%d",&a[i]);
			sum+=i*a[i];
			if(!flag && a[i]) flag=1;
		}
		
		if(!flag) break;
		
		printf("Collection #%d:\n",t);

		if(sum&1){
			printf("Can't be divided.\n\n");
			continue;
		}
		sum=sum>>1;
		if(dfs(1,0))
			printf("Can be divided.\n\n");
		else
			printf("Can't be divided.\n\n");
	}
	return 0;
}


 

 下面是模拟多重背包问题,总共6个物品,将第i个物品的价值与费用都看做是i,这样套用背包九讲的多重背包模板就可以了,不过注意的是数组要开的大一点.

#include <iostream>
using namespace std;
#define MAXV 100005
#define INF 100000000
#define max(a,b) a>b?a:b

int sum,a[7],f[MAXV];

void CompletePack(int cost,int weight){
	int i;
	for(i=cost;i<=sum;i++){
		f[i]=max(f[i],f[i-cost]+weight);
	}
}
void ZeroOnePack(int cost,int weight){
	int i;
	for(i=sum;i>=cost;i--){
		f[i]=max(f[i],f[i-cost]+weight);
	}
}

void MultiplePack(int cost,int weight,int amount){
	if(cost*amount>=sum){
		CompletePack(cost,weight);
		return ;
	}
	int k=1;
	while(k<amount){
		ZeroOnePack(k*cost,k*weight);
		amount-=k;
		k=k<<1;
	}
	ZeroOnePack(amount*cost,amount*weight);
}

void dp(){
	int i,j,k;
	for(i=1;i<=sum;i++) f[i]=-INF;
	f[0]=0;
	for(i=1;i<=6;i++)
		MultiplePack(i,i,a[i]);
}

int main(){
	int flag,i,t;
	for(t=1;;t++){
		for(flag=sum=0,i=1;i<=6;i++){
			scanf("%d",&a[i]);
			sum+=i*a[i];
			if(!flag && a[i]) flag=1;
		}
		
		if(!flag) break;
		
		printf("Collection #%d:\n",t);
		
		if(sum&1){
			printf("Can't be divided.\n\n");
			continue;
		}
		sum=sum>>1;
		dp();
		if(f[sum]>=0)
			printf("Can be divided.\n\n");
		else
			printf("Can't be divided.\n\n");
	}
	return 0;
}

 

 

上面是用“拆分法”来做的,不过应为题目是一个可行性问题,即状态只有两种,是否可行,现在用一种O(VN)的算法来写,上面O(V*∑logn[i])跑了16MS,现在用这种算法只跑了0MS。或许是这题的数据太弱了吧。

 


 

#include <iostream>
using namespace std;
#define MAXV 100005
#define INF 100000000
#define max(a,b) a>b?a:b

int sum,a[7],ans,f[MAXV],t[MAXV];

void dp(){
	int i,j,k;
	memset(f,0,sizeof(f));		//用1表示可行,0表示不行
	f[0]=1;
	for(i=1;i<=6;i++){
		memset(t,0,sizeof(t));
		for(j=i;j<=sum;j++){
			if(!f[j] && f[j-i] && t[j-i]+1<=a[i]){
				f[j]=1;
				t[j]=t[j-i]+1;
			}
		}
	}
}

int main(){
	int flag,i,t;
	for(t=1;;t++){
		for(flag=sum=0,i=1;i<=6;i++){
			scanf("%d",&a[i]);
			sum+=i*a[i];
			if(!flag && a[i]) flag=1;
		}
		
		if(!flag) break;
		
		printf("Collection #%d:\n",t);
		
		if(sum&1){
			printf("Can't be divided.\n\n");
			continue;
		}
		sum=sum>>1;
		dp();
		if(f[sum])
			printf("Can be divided.\n\n");
		else
			printf("Can't be divided.\n\n");
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值