https://vjudge.net/problem/HDU-2844
题目大意
给出一些硬币的数值和数量,给定一个上限m,求这些硬币在上限m以下能够覆盖的钱数的数量。
思路
多重背包用在这题目还是很巧妙的。(事实上有不用背包的dp算法,但是不够快)最关键的是一个dp[i]==i的细节,比较有趣。
多重背包之所以快还是那个二进制优化。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m,dp[maxn],a[105],c[105],sum;
///用多重背包解决硬币问题
///dp[k]的含义:用容量为k的背包能够得到的价值为多少
///如果dp[k]==k,说明背包可以装得下k元钱,则这个钱数是取的到的
void CompletePack(int w,int v)
{
int i;
for(i=w;i<=sum;i++)
{
if(dp[i]<dp[i-w]+v)
{
dp[i]=dp[i-w]+v;
}
}
}
void ZeroOnePack(int w,int v)
{
int i;
for(i=sum;i>=w;i--)
{
if(dp[i]<dp[i-w]+v)
{
dp[i]=dp[i-w]+v;
}
}
}
void MultiplePack(int w,int v,int num)
{
if(w*num>=sum)
{
CompletePack(w,v);
return;
}
else
{
int k=1;
while(k<=num)
{
ZeroOnePack(k*w,k*v);
num-=k;
k*=2;
}
ZeroOnePack(num*w,num*v);
}
}
int main()
{
while(~scanf("%d%d",&n,&m)&&n+m)
{
memset(dp,0,sizeof(dp));
int i;
sum=0;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(i=1;i<=n;i++)
{
scanf("%d",&c[i]);
sum+=a[i]*c[i];
}
sum=min(sum,m);
for(i=1;i<=n;i++)
{
MultiplePack(a[i],a[i],c[i]);
}
int ans=0;
for(i=1;i<=sum;i++)
{
if(dp[i]==i) ans++;///dp上可能有数字,但是不一定等于i
}
printf("%d\n",ans);
}
return 0;
}