Coins
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3424 Accepted Submission(s): 1345
You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.
这道题要求的是用所有的硬币在1到m的范围内最多能够组合成多少种价值.每种硬币有数量的限制.
对于每种硬币而言:
if 价值×数量>=m
then 取这种硬币的次数相当于无限制,可以考虑成完全背包
else
then 考虑成0-1背包(二进制优化),就是把这种硬币的value和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之后就变成了一个01背包).
对于多重背包问题可以这样解:
MultiplePack(cost,weight,amount)
{
if(cost*weight>=V)
CompletePack(cost,weight)
else
{
for(int i=1;i<amount;i<<1)
{
ZeroOnePack(i*cost,i*weight);
amount-=i;
}
ZeroOnePack(amount*cost,amount*weight)
}
}
这道题的cost和weight是同一种量.
#include<iostream> using namespace std; int dp[100010]; int a[110],c[110]; int n,m; void ZeroOnePack(int c,int w)//01背包 { for(int i=m;i>=c;i--) dp[i]=dp[i]|dp[i-c];
/*等价于dp[i]=Max(dp[i],dp[i-c]);*/; } void CompletePack(int c,int w)//完全背包 { for(int i=c;i<=m;i++) dp[i]=dp[i]|dp[i-c]; } void MutiplePack(int c,int w)//多重背包 { int temp; if(c*w>=m)//如果物品的总花费大于背包容量就利用完全背包解决 CompletePack(c,w); else//否则利用01背包解决(二进制思想优化) { temp=w; for(int i=1;i<=temp;i<<2) { ZeroOnePack(i*c,i*w); temp-=i; } ZeroOnePack(temp*c,temp*w); } } int main() { int i; while(scanf("%d%d",&n,&m),n||m) { memset(dp,0,sizeof(dp)); for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=1;i<=n;i++) scanf("%d",&c[i]); dp[0]=1; for(i=1;i<=n;i++) MutiplePack(a[i],c[i]); int ans=0; for(i=1;i<=m;i++) if(dp[i]) ans++; printf("%d\n",ans); } return 0; }