第五届省赛题 Divideing Jewels


Divideing Jewels


时间限制:1000 ms  |  内存限制:65535 KB
难度:4
描述
Mary and Rose own a collection of jewells. They want to split the collection among themselves so that both receive an equal share of the jewels. This would be easy if all the jewels had the same value, because then they could just split the collection in half. But unfortunately, some of the jewels are larger, or more beautiful than others. So, Mary and Rose start by assigning a value, a natural number between one and ten, to each jewel. Now they want to divide the jewels so that each of them gets the same total value. Unfortunately, they realize that it might be impossible to divide the jewels in this way (even if the total value of all jewels is even). For example, if there are one jewel of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the jewels.
输入
Each line in the input file describes one collection of jewels to be divided. The lines contain ten non-negative integers n1 , . . . , n10 , where ni is the number of jewels of value i. The maximum total number of jewells will be 10000. 
The last line of the input file will be "0 0 0 0 0 0 0 0 0 0"; do not process this line. 
输出
For each collection, output "#k:", where k is the number of the test case, and then either "Can be divided." or "Can't be divided.". 
Output a blank line after each test case. 
样例输入
1 0 1 2 0 0 0 0 2 0
1 0 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
样例输出
#1:Can't be divided.
#2:Can be divided.
目标: 只要能组合出总价值的一半就可以了(由于一人一半,能分出来才行),那么就成背包问题了,将总价值的一半看成是 重量 ,求最大价值 。(当然 这里的最大价值 肯定就是重量了 ,绝对是超不过重量的),,这个题把物品看成 重量和价值是一样的,就可以递推了。(并没有违背 一个宝石是不可分割的 )
   1.多重背包问题 转化为  0-1背包问题 ,把数量分解(这题必然涉及到拆分,并不是取全部的量 ,是有选择性的取一部分,那么目标就是拆成 能组成各种情况的物品 ,满足我们可能需要的这一部分),将这n= 10 种物品,分成多种物品 ,涉及到二进制,多么奇妙。
       二进制原理:
       1,2 4,8 ,,,,2^n   ,任意之和可以组合成任何一个数。(排列组合)
       1 ------> 0001
       2 ------> 0010
       3-------> 0011
       ...
       相当于从全0000  到全 1111(数之和的结果,当然0在本题里无意义)
那么就可以开始,现将 a[i] * i 分解成各种种类的 物品的
将原重量 为 i*a[ i ] 价值为 i*a[ i ] 的一个物品  分成  重量为 i*j  价值 为 i*j 的物品     
k= 0;for( i = 1 ;i <= 10;i++)
{
for( j = 1;j <= a[i] ;j *= 2) // j = 1,2,4,8...
{
v[k++] = i*j;
a[i] -=  j;
}
if(a[i] >0 )
{
v[k++] = a[i]*i;
}
}
之后 ,用递推去增加元素不断更新 , 去实现某种意义上的组合
第一种方法:
#include<stdio.h>
#include<string.h>
#define max(a,b) a>b?a:b

int v[100000];int dp[100000];
int main()
{

	int i,j,k,sum,cent,d=0;
	int a[11];
	while(1)
	{
		memset(dp,0,sizeof(dp));
		sum = 0;cent = 0;
		for(i = 1; i<= 10 ; i++)
		{
			scanf("%d",&a[i]);
			sum += a[i]*i;
			if(a[i] == 0)cent++;
		}
        if(cent == 10)break;
		k = 0;
		if(sum%2)
		{
			printf("#%d:Can't be divided.\n",++d);
			continue;
		}

		for( i = 1 ;i <= 10;i++)
		{
			for( j = 1;j <= a[i] ;j *= 2)
			{
				v[k++] = i*j;
				a[i] -=  j;
			}
			if(a[i] >0 )
			{
				v[k++] = a[i]*i;
			}
		}


		for( i = 0;i <  k ;i++)
		{
			for(j = sum/2; j>=v[i] ; j--)
			{
				dp[j] = max(dp[j] ,dp[j-v[i]]+v[i]);
			}
		}


		if(dp[sum/2]*2 ==sum)printf("#%d:Can be divided.\n",++d);
		else printf("#%d:Can't be divided.\n",++d);
	}


	return 0;
}
2.多重背包 可以看成是  完全背包 和 0-1 背包的 的组合
 
   0 - 1 背包:  倒着推 (原理 目的:只能用一次 )
   完全背包 :  正着推 (原理 目的:以前一次为基础,还能接着重复用 ,每一次用前一次的累积,变成了重复用该物品 )
                          什么时候用  0-1背包: 当物品不足够的时候, 就要有选择性的取。
                         完全背包: 当物品非常充足,足够你用,那你想则么用怎么用。

方法二:
#include<stdio.h>
#include<string.h>
#define max(a,b) a>b?a:b

int dp[50001];
int sum;
void  z_o ( int value )
{
	int j,weight =  value;
	for( j = weight; j <= sum/2 ; j++ )
		dp[j] = max( dp[j] ,dp[j- weight] + value );
}

void full ( int value )
{
	int j,weight =  value;
	for( j = sum/2; j >= weight ;j--)
		dp[j] = max( dp[j] ,dp[j- weight] + value );
}


void d_p( int value , int count )
{
	int k;
	if( value * count < sum/2)  //有选择性的取,(枚举)各种情况组合(通过不断加组元素更新来达到某种意义上的组合),能更新出最大的价值
	{
		k = 1;
		for( ;k <= count;k =k*2)
		{
			full( k*value );
			count -= k;
		}
		if(count >0 )
		{
		    full( count * value );
		}

	}
	 else
	 {
		 z_o(value);        //数量无限,为了取最大的价值,想怎么更新就怎么更新,正推

	 }


}


int main()
{

	int i,j;
    int n = 10,count=0,d = 0;

	int a[11];
	while(1)
	{
		sum = 0;
		count = 0;
		memset(dp,0,sizeof(dp));
		for(i = 1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if(a[i] == 0) count++;

			sum += a[i]*i;
		}

		
		if(count== 10)break;

		if(sum % 2) 
		{
			printf("#%d:Can't be divided.\n",++d);
			continue;
		}
		for(i = 1;i<=n ;i++)
			d_p( i,a[i]);


		if( dp[sum/2]*2 == sum )printf("#%d:Can be divided.\n",++d);
			else printf("#%d:Can't be divided.\n",++d);

	}

	return 0;
}




   



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值