【poj1014】 Dividing

时间:2023-03-09 02:02:51
【poj1014】 Dividing

http://poj.org/problem?id=1014 (题目链接)

题意

  给出有分别价值为1,2,3,4,5,6的6种物品,输入6个数字,表示相应价值的物品的数量,问一下能不能将物品分成两份,是两份的总价值相等。

solution

  多年以前写的程序了,现在才写博客= =。这道题一看就是多重背包,所以我们用二进制把它拆分成01背包就很好做了,不知道的话就看《背包九讲》吧。。

  首先我们把6种物品的总价值记为S,如果S%2==1,那么显然是无解的。考虑S%2==0的情况。我们把每个种物品用二进制法分解成若干个物品,用二进制分解的话,复杂度就是O(nlogn)的,而且也不用担心会有些情况考虑不到。之后背包dp,判断f[S/2]是否等于S/2即可。

代码

// poj1014
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define Pi 3.1415926535898
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; int f[1000010];
int T,a[10],num[1000010],tot; void tt(int w,int v) {
int i=0,x,tmp=0;
while (1) {
x=1<<i;
if (tmp+x>v) break;
tmp+=x;
num[++tot]=x*w;
i++;
}
x=v-tmp;
if (x>0) num[++tot]=x*w;
}
int main() {
T=0;
while (++T) {
memset(f,0,sizeof(f));
int s=0;
for (int i=1;i<=6;i++) {
scanf("%d",&a[i]);
s+=a[i]*i;
}
if (s==0) break;
printf("Collection #%d:\n",T);
if (s%2==1) {
printf("Can't be divided.\n\n");
continue;
}
int mid=s/2;
tot=0;
for (int i=1;i<=6;i++) tt(i,a[i]);
for (int i=1;i<=tot;i++)
for (int j=mid;j>=num[i];j--)
f[j]=max(f[j],f[j-num[i]]+num[i]);
if (f[mid]==mid) printf("Can be divided.\n\n");
else printf("Can't be divided.\n\n");
}
return 0;
}