三种解法:多重背包转化为完全背包和01背包;多重背包通过二进制化化为01背包;通过计数法优化为2重循环。
code1: 47MS
#include <cstdio> #include <cstring> #define Max(a,b) (a) >(b)?(a):(b) int dp[100005], Cost[11], Count[11], cash; void ZeroOnePack(int cost) { int i; for(i=cash; i>=cost; --i) dp[i] = Max(dp[i], dp[i-cost]+cost); } void CompletePack(int cost) { int i; for(i=cost; i<=cash; ++i) dp[i] = Max(dp[i], dp[i-cost]+cost); } void MultiplePack(int cost, int count) { if(cost*count > cash) CompletePack(cost); else { int k=1; while(k < count) { ZeroOnePack(k*cost); count -= k; k <<=1; } ZeroOnePack(count*cost); } } int main() { int n, i, j, k; while(~scanf("%d%d",&cash,&n)) { for(i=1; i<=n; ++i) scanf("%d%d",&Count[i],&Cost[i]); memset(dp, 0, sizeof dp ); for(i=1; i<=n; ++i) MultiplePack(Cost[i], Count[i]); printf("%d\n", dp[cash]); } return 0; }
code2: 110MS
#include <cstdio> #include <cstring> #define Max(a,b) (a) >(b) ? (a):(b) int dp[100010]; int n[230], w[230], v, N; int b[11] = {1,2,4,8,16,32,64,128,256,512,1024}; int main() { int i, j, cash, Count, k; int nn, ww; while(~scanf("%d%d",&cash,&N)) { Count = 1; for(i=1; i<=N; ++i) { scanf("%d%d",&nn,&ww); if(nn!=0) { for(j=10; j>=0; --j) if(nn-b[j]+1>0) break; for(k=0; k<=j-1; ++k) { n[Count] = b[k]; w[Count] = ww*b[k]; Count++; } n[Count] = nn-b[j]+1; w[Count]=ww*(nn-b[j]+1); Count++; } } Count--; memset(dp, 0, sizeof dp ); for(i=1; i<=Count; ++i) for(v=cash; v>=0; --v) if(v-w[i]>=0) dp[v] = Max(dp[v], dp[v-w[i]] + w[i]); printf("%d\n", dp[cash]); } return 0; }
//1、可以采有计数的方法代替单调队列将时间复杂度降到o(vn)
//注:计数的这种方法解决多重背包时有限制,只能解决体积与价值相等的情况
#include <cstdio> #include <cstring> int main() { int i, j, cash, n, v[15], nums[15]; int opt[100010]; int cnt[100010]; while(~scanf("%d%d",&cash,&n)) { for(i=0; i<n; ++i) scanf("%d%d",&nums[i],&v[i]); for(i=0; i<=cash; ++i) opt[i] = 0; for(i=0; i<n; ++i) { memset(cnt, 0, sizeof(int)*(cash+1)); for(j=v[i]; j<=cash; ++j) { if(opt[j] <opt[j-v[i]]+v[i]&&cnt[j-v[i]]<nums[i]) { cnt[j] = cnt[j-v[i]]+1; opt[j] = opt[j-v[i]] + v[i]; } } } printf("%d\n",opt[cash]); } return 0; }