HDU2844 Coins(多重背包)
多重背包就是每种物品有数量限制时求解最大价值。
如果一种物品数量和重量之积超过背包容量,可视为完全背包;其余情况通过二进制拆分,将几个数量的物品看成一个,转化为01背包求解。
按照这种思路代码是这样的:
1 #include<cstdio>//多重背包,二进制拆分 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define M 100005 6 bool dp[M];//dp[j]表示和为j的组合方式是否可行 7 int v[105],c[105];//价值,数量 8 9 void multi_knapsack(int n,int m){ 10 for(int i=1;i<=n;i++){ 11 if(c[i]*v[i]>=m){//转化为完全背包 12 for(int j=v[i];j<=m;j++) 13 if(dp[j-v[i]]) dp[j]=1; 14 } 15 else{ 16 for(int k=1;c[i]>0;k<<=1){ 17 int x=min(k,c[i]); 18 for(int j=m;j>=v[i]*x;j--)//转化为01背包 19 if(dp[j-v[i]*x]) dp[j]=1; 20 c[i]-=x; 21 } 22 } 23 } 24 } 25 26 int main(){ 27 int n,m; 28 while(~scanf("%d%d",&n,&m),n+m){ 29 for(int i=1;i<=n;i++) scanf("%d",&v[i]); 30 for(int i=1;i<=n;i++) scanf("%d",&c[i]); 31 memset(dp,0,sizeof(dp)); 32 dp[0]=1;//初始状态0可达 33 multi_knapsack(n,m); 34 int ans=0; 35 for(int i=1;i<=m;i++) 36 ans+=dp[i]; 37 printf("%d\n",ans); 38 } 39 return 0; 40 }
注意到这道题是可行性问题,可以用一个数组used[j]记录拼成价值j是用了多少个第i种物品,实现数量限制约束。
1 #include<cstdio>//数组优化 2 #include<cstring> 3 using namespace std; 4 #define M 100005 5 int v[105],c[105],used[M]; 6 bool dp[M]; 7 8 int main(){ 9 int n,m,ans; 10 while(~scanf("%d%d",&n,&m),n&&m){ 11 for(int i=1;i<=n;i++) scanf("%d",&v[i]); 12 for(int i=1;i<=n;i++) scanf("%d",&c[i]); 13 memset(dp,0,sizeof(dp)); 14 ans=0,dp[0]=1; 15 for(int i=1;i<=n;i++){ 16 memset(used,0,sizeof(used)); 17 for(int j=v[i];j<=m;j++) 18 if(!dp[j]&&dp[j-v[i]]&&used[j-v[i]]<c[i]){ 19 dp[j]=1; 20 used[j]=used[j-v[i]]+1; 21 ans++; 22 } 23 } 24 printf("%d\n",ans); 25 } 26 return 0; 27 }