【POJ1014】Dividing 多重揹包,二進位制物品拆分轉01揹包
阿新 • • 發佈:2018-12-24
直接做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; }