原题链接:点击此处
解题思路:
多重背包问题,不太理解,理解了再补充。
网上博客的思路是:
对于每个硬币而言: IF 价值×数量>=m THEN 取这个硬币的次数相当于无限制,可以考虑成完全背包 ELSE THEN 考虑成0-1背包(二进制优化),就是把这个硬币的v和num组合出0-1背包可能出现的状态(可以去看背包九讲) (对于num,类似于编码。 当2^n <=num/2时:k = 2^n(n=0,1,2,……)表示状态,对应下来就是二进制的某一位数是1,然后还有一个状态就是k>num/2的时候啦,num+1-k,这样下来就可以用k来组合枚举出从1->num的所有可能了。然后对于k,单位价值和大小都乘上k之后就变成了一个0-1背包)
源代码如下:
#include <stdio.h> #include <algorithm> #include <string.h> using namespace std; const int MAX=100000; int dp[MAX]; int c[MAX],w[MAX]; int v; void ZeroOnePack(int cost,int wei)//01 { int i; for(i = v;i>=cost;i--) { dp[i] = max(dp[i],dp[i-cost]+wei); } } void CompletePack(int cost,int wei)//完全 { int i; for(i = cost;i<=v;i++) { dp[i] = max(dp[i],dp[i-cost]+wei); } } void MultiplePack(int cost,int wei,int cnt)//多重 { if(v<=cnt*cost) { CompletePack(cost,wei); return ; } else { int k = 1; while(k<=cnt) { ZeroOnePack(k*cost,k*wei); cnt = cnt-k; k = 2*k; } ZeroOnePack(cnt*cost,cnt*wei); } } int main() { int n; while(~scanf("%d%d",&n,&v),n+v) { int i; for(i = 0;i<n;i++) scanf("%d",&c[i]); for(i = 0;i<n;i++) scanf("%d",&w[i]); memset(dp,0,sizeof(dp)); for(i = 0;i<n;i++) { MultiplePack(c[i],c[i],w[i]); } int sum = 0; for(i = 1;i<=v;i++) { if(dp[i]==i) { sum++; } } printf("%d ",sum); } return 0; }