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: ",T); if (s%2==1) { printf("Can't be divided. "); 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. "); else printf("Can't be divided. "); } return 0; }