HDU - 1059 Dividing

版权声明:博客作者为blue bear,严禁他人在未经作者同意下进行商用转载 https://blog.csdn.net/weixin_44354699/article/details/88233987

01背包 + 二进制拆分

题目链接

题面

Problem Description
Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value.
Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble 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 marbles.

Input
Each line in the input describes one collection of marbles to be divided. The lines consist of six non-negative integers n1, n2, …, n6, where ni is the number of marbles of value i. So, the example from above would be described by the input-line “1 0 1 2 0 0’’. The maximum total number of marbles will be 20000.
The last line of the input file will be “0 0 0 0 0 0’’; do not process this line.

Output
For each colletcion, output“Collection #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
1 0 0 0 1 1
0 0 0 0 0 0
Collection #1:
Can’t be divided.
Collection #2:
Can be divided.

题意

给6个整数,代表硬币面额1,2,3,4,5,6的数量。问你能否划分为等额的两份。6个0代表输入结束

分析

显然可以知道总和为奇数的时候不可能均分成两份。
总和为偶数(2N)的时候,只需要找出一堆等于N的,另外一堆就自然是N了。
这个时候就可以用01背包1看能否恰好达到N。
但是如果你把n个硬币拆成1个1个的话,很明显会超时
此时就需要我们标题所讲的,二进制拆分
举个例子13=1+2+4+6
你会问为什么是6不是8,因为如果是8的话总和超过13显然不是我们要的结果,而有了1,2,4,6我们就可以通过组合获得1,2,3…13
而20000就可以拆成

20000=1+2+4+8+16+32+64+128+256+512+1024+2048+4096+8192+3617

比20000个1快了许多。(N->logN)
而二进制拆分的原理嘛就是保证每个二进制数位都有一个数1,10,100,1000…
再把剩余那个数自成一个数,这样结合的时候对应数位就可以随意组合了。
下面这个代码可以试试

#include <stdio.h>
int main(){
	int x;
	while(~scanf("%d",&x)){
		printf("%d=",x);
		for(int i=1;i<=x;i*=2) {
        	printf("%d+",i);
			x-=i;
		}
    	if(x>0) printf("%d",x);
    	puts("");
	}
	return 0;
}

代码

#include <stdio.h>
#include <cmath>
#define max(a,b) ( (a)>(b)?(a):(b) )
#define INF 0x3f3f3f3f
int main(){
	int count=1,a[7],i,j,num,sum,V,v[120005],n,f[120005],t;
	while(scanf("%d%d%d%d%d%d",&a[1],&a[2],&a[3],&a[4],&a[5],&a[6])){
		for(sum=0,i=1;i<7;i++) sum+=i*a[i];
		if( sum==0 ) break;
		if(sum%2==1){
			printf("Collection #%d:\nCan't be divided.\n\n",count++);
			continue;
		}
		V=sum/2;
		sum=0;
		for(n=i=1;i<7;i++){
			for(j=1;j<=a[i];j*=2) {
                v[n++]=j*i;
                a[i]-=j;
            }
            if(a[i]>0) v[n++]=a[i]*i;
        }
		for(f[0]=0,i=1;i<=V;i++) f[i]=-INF;
		for(i=1;i<n;i++){
			for(j=V;j>=v[i];j--){
				f[j]=max(f[j],f[j-v[i]]+v[i]);
			}
		}
		if(f[V]<0) printf("Collection #%d:\nCan't be divided.\n\n",count++);
		else printf("Collection #%d:\nCan be divided.\n\n",count++);
	}
	return 0;
}

  1. 01背包就是dp问题的一种,可见我的博客文章,或者搜索背包九讲深度了解,本篇不再赘述。 ↩︎

猜你喜欢

转载自blog.csdn.net/weixin_44354699/article/details/88233987