使用一种二进制的优化, 可以完美的解决这题,《背包九讲》中说的非常好
但是还有一种线性复杂的算法。 应该算是该题很巧妙的解法
for(i=1;i<=6;i++) { for(l=total;l>=0;l--) { if(dp[l]==0) continue; for(k=1;k<=num[i]&&k*i+l<=total;k++) { if(dp[k*i+l]) break; // 这个剪枝瞬间将复杂度从N^2变成了N。 dp[k*i+l]=1; } } }
代码中total是我们要装满的容量, 循环的次序很重要。 当关键还是那一步剪枝
在附上一份用二进制优化多重背包的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include <math.h> #include <map> #include <queue> #include <sstream> #include <iostream> using namespace std; #define INF 0x3fffffff struct node { int w,c; }g[10010]; //将log2压缩后的元素存在这里! int k[10]; int dp[2002000]; int mpow(int x) { int sum=1; for(int i=0;i<x;i++) sum=sum*2; return sum; } int main() { //freopen("//home//chen//Desktop//ACM//in.text","r",stdin); //freopen("//home//chen//Desktop//ACM//out.text","w",stdout); int tt=1; while(1) { int tmp=0,sum=0; for(int i=1;i<=6;i++) { scanf("%d",&k[i]); tmp+=k[i]; sum+=k[i]*i; } if(tmp==0) break; printf("Collection #%d: ",tt++); if(sum%2!=0) { printf("Can't be divided. "); printf(" "); continue; } int cnt=0; for(int i=1;i<=6;i++) { if(k[i]==0) continue; tmp=0; int tk=1; while(2*tk-1 < k[i]) { g[cnt].w=1; g[cnt].c=i*tk; cnt++; tmp+=tk; tk*=2; } g[cnt].w=1; g[cnt].c=i*(k[i]-tmp); cnt++; } for(int i=1;i<=sum/2;i++) { dp[i]=-INF; } dp[0]=0; for(int i=0;i<cnt;i++) { for(int j=sum/2;j>=g[i].c;j--) { dp[j]=max(dp[j],dp[j-g[i].c]+g[i].w); } } if(dp[sum/2]>=0) printf("Can be divided. "); else printf("Can't be divided. "); printf(" "); } return 0; }