直接做01背包,即把物品数量累加,做20000物品的01背包指定TLE,不用我说了吧!
本文的优化是二进制优化,O(logn),至于完全背包记录已使用个数的O(n)算法本文不进行讲解,在博客的“背包”分类里。
二进制优化:
大家知道一个十进制数可以转换成二进制,那么假设某种物品有1023种,即2^10-1,二进制为111111111,则可以视为每一位分别是一个由{1,2,4,8,16,32,64,128,256,512}个物品糅合成的大物品,二进制数每一位0表示不取,1表示取,这样我们就可以取遍所有的数,而若不是2的整数幂-1个物品,再补上缺的物品个数就好了,比如1025,就再补上个由2个物品组成的新物品。
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n[7],sum; bool f[121000]; int main() { // freopen("test.in","r",stdin); int i,j,k,g; int flag; for(g=1;;g++) { flag=sum=0; for(i=1;i<=6;i++) { scanf("%d",&n[i]); if(n[i])flag=1,sum+=n[i]*i; } if(!flag)return 0; printf("Collection #%d:\n",g); if(sum&1) { printf("Can't be divided.\n\n"); continue; } sum>>=1; memset(f,0,sizeof(f)); f[0]=1; for(i=1;i<=6;i++) { /*二进制拆分*/ int temp; for(j=0;(1<<j)<n[i];j++) {/*手模拟这个for循环(1<<j)的值,很好懂。*/ n[i]-=(1<<j); temp=(1<<j)*i; for(k=sum-1;k>=0;k--)if(f[k]&&k+temp<=sum)f[k+temp]=1; if(f[sum])break; } if(n[i]) {/*同题解说的“1025”那种情况*/ temp=n[i]*i; for(k=sum-1;k>=0;k--) if(f[k]&&k+temp<=sum) f[k+temp]=1; } /**/ if(f[sum])break; } if(f[sum])printf("Can be divided.\n\n"); else printf("Can't be divided.\n\n"); } return 0; }
【POJ1014】Dividing 多重背包,二进制物品拆分转01背包
原文地址:http://blog.csdn.net/vmurder/article/details/39472419