多重背包POJ1276不要求恰好装满 poj1014多重背包恰好装满
二进制压缩,看背包九讲啦,写的很详细了,不赘述````
先贴不要求恰好装满的情形
POJ 1276
二进制压缩:
1 #include <cstdio> 2 #include <cstring> 3 int f[100005];//容量为i时最多能装多少 4 int p[12000];//物品拆分 5 int main() 6 { 7 int n,cash; 8 while(~scanf("%d%d",&cash,&n)) 9 { 10 int vi,num;//物品价值,数量 11 int cur = 0; 12 memset(f,0,sizeof(f)); 13 for(int i=0; i<n; i++) 14 { 15 scanf("%d%d",&num,&vi); 16 int k = 1; 17 //把物品拆分成num = 1 + 2 + 4 + 8 + 16 + 32 + ``` + 2^k + l(剩余的值) 18 //假设某物品有10件,拆成7=1+2+4+3。。这样,10件这样的物品变成了4件物品,用这四种物品可组和出 19 //0-10的每一种情形,原理在于二进制,每位取0或者1,但是能表示出很多数 20 //多重背包转化成了0-1背包 21 while(num-k > 0) 22 { 23 p[cur]= vi*k; 24 num -= k; 25 k *= 2; 26 cur++; 27 } 28 p[cur] = vi*num; 29 cur++; 30 } 31 for(int i=0; i< cur; i++) 32 { 33 for(int j = cash; j >= p[i]; j--) 34 { 35 if(f[j] < f[j-p[i]]+p[i]) 36 f[j] = f[j-p[i]]+p[i]; 37 } 38 } 39 printf("%d\n",f[cash]); 40 } 41 return 0; 42 }
另一种算法,感觉挺好的。。。
1 #include <cstdio> 2 #include <cstring> 3 //#define debug 4 int count[100005];//被使用的次数 5 bool vis[100005]; //是否恰好能被零散货币表示 6 int main() 7 { 8 int n,i,j,cash; 9 #ifdef debug 10 freopen("in.cpp","r",stdin); 11 #endif 12 while(~scanf("%d",&cash)) 13 { 14 scanf("%d",&n); 15 memset(vis,0,sizeof(vis));//vis用来表示是否恰好被装满 16 vis[0] = 1; 17 for(i=0; i<n; ++i) 18 { 19 int v,num; 20 scanf("%d%d",&num,&v); 21 memset(count,0,sizeof(count));//用来记录该物品被用过几次,每次都得重新赋值,麻烦的 22 for(j=v; j<=cash; ++j) 23 { 24 int t = j-v; 25 if(!vis[j] && vis[t] && count[t] < num)//用过的次数>=num。就不行了 26 { 27 vis[j] = 1; 28 count[j] = count[t] + 1; 29 } 30 } 31 } 32 for(i=cash; i>=0; --i) 33 { 34 if(vis[i]) 35 { 36 printf("%d\n",i); 37 break; 38 } 39 } 40 } 41 return 0; 42 }
该算法复杂度为O(N*M),貌似和单调队列优化的复杂度差不多
再贴恰好要装满的情形
POJ 1014
View Code
1 #include <cstdio> 2 int f[500000]; 3 int p[2000]; 4 int main() 5 { 6 int ser = 0; 7 while(1) 8 { 9 int n[7]; 10 int sum =0; 11 int cur =0; 12 for(int i=1; i<= 6; i++) 13 { 14 scanf("%d",&n[i]); 15 sum += i*n[i]; 16 int k = 1; 17 while(n[i] - k > 0) 18 { 19 p[cur] = i*k; 20 cur++; 21 n[i] -= k; 22 k *= 2; 23 } 24 p[cur] = n[i]*i; 25 cur++; 26 } 27 if(sum == 0) break; 28 if(sum%2 == 1) 29 { 30 printf("Collection #%d:\n",++ser); 31 printf("Can't be divided.\n\n"); 32 continue; 33 } 34 sum = sum/2; 35 for(int i=1 ; i <= sum; i++) 36 f[i] = -1000; 37 f[0] = 0; 38 for(int i=0; i < cur && f[sum] < 0; i++) 39 { 40 for(int j= sum; j >0; j--) 41 { 42 if(j - p[i] < 0) continue; 43 if(f[j-p[i]] >= 0 && f[j-p[i]]+p[i] > f[j]) 44 { 45 f[j] = f[j-p[i]]+p[i]; 46 if(j == sum ) break; 47 } 48 } 49 } 50 printf("Collection #%d:\n",++ser); 51 if(f[sum] == sum) 52 printf("Can be divided.\n\n"); 53 else 54 printf("Can't be divided.\n\n"); 55 } 56 return 0; 57 }