http://poj.org/gotoproblem?pid=1742
(1)多重背包的处理方式有两种:
1)转化为分组背包。本题用到了将硬币数转化成“1,2,4,8,... ,余数”的形式。
2)转化为01背包。再开一个数组,记录每一个dp[i] 对每一种硬币的使用情况,以确保不多用硬币。
(2)以运行效率而言,上一条中2)的效率高一些(我的代码前者超时,后者AC,具体原因不是特别明确)。似乎本题就是在卡时间,在时间细节设陷阱,各位请注意。
以下代码用的是转化为01背包。
具体代码:
View Code
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int Inf = 1<<29; int n, m; int p[110], num[110]; int dp[100100], used[100100]; int main() { int i, j, k; while(scanf("%d%d", &n, &m)!=EOF, n||m) { for(i=1;i<=n;i++) scanf("%d", &p[i]); for(i=1;i<=n;i++) scanf("%d", &num[i]); memset(dp, 0, sizeof(dp)); dp[0]=1; int sum=0; for(i=1;i<=n;i++) { memset(used, 0, sizeof(used)); for(j=p[i];j<=m;j++) if(!dp[j]&&dp[j-p[i]]&&used[j-p[i]]+1<=num[i]) dp[j]=1, used[j]=used[j-p[i]]+1, sum++; } printf("%d\n", sum); } return 0; }
超时代码,用的是转化为分组背包:
View Code
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int Inf = 1<<29; int n, m; int p[101], num[101], map[101][11]; bool dp[100001]; int main() { int i, j, k; while(scanf("%d%d", &n, &m)!=EOF, n||m) { for(i=1;i<=n;i++) scanf("%d", &p[i]); memset(map, 0, sizeof(map)); for(i=1;i<=n;i++) // 转化为分组背包 { scanf("%d", &num[i]); int x=1, tot=0, tm=num[i]; while(tm>0) { if(tm<x) {map[i][tot++]=tm;break;} map[i][tot++]=x; tm-=x; x*=2; } } fill(dp, dp+100001, 0); dp[0]=1; for(i=1;i<=n;i++) for(j=0;map[i][j];j++) for(k=m;k>=map[i][j]*p[i];k--) dp[k]=dp[k]||dp[k-map[i][j]*p[i]]; int sum=0; for(i=1;i<=m;i++) if(dp[i]) sum++; printf("%d\n", sum); } return 0; }
