HDU 2844 Coins 多重背包
HDU 2844 Coins 多重背包
题意
给你n种硬币,每个硬币的金额为\(Ai\),数量为\(Ci\),然后给你一个范围\(m\),问在使用这些硬币可以组成多少种金额,要求组成的金额在\((1, m)\)范围内。
解题思路
使用多重背包来进行解决这个问题。这样我们能够获得每种金额(可以看作背包的容量)下使用硬币组成的最多的金额数量。二重背包结束后,然后使用for循环,遍历这里dp里面\(dp[i]==i\)的个数,这个个数就是答案。
代码实现
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=107; //硬币的种类数量
const int maxm=1e5+7; //金额的最大范围
int a[maxn], c[maxn];
int n, m;
int dp[maxm];
int main()
{
while(scanf("%d%d", &n, &m) && (n+m)!=0)
{
for(int i=1; i<=n; i++)
scanf("%d", &a[i]);
for(int i=1; i<=n; i++)
scanf("%d", &c[i]);
memset(dp, 0, sizeof(dp));
//下面就是二重背包的主要代码,可以说就是模板了
for(int i=1; i<=n; i++) //遍历前i种硬币
{
int min_num=min(c[i], m/a[i]); //选择这种硬币的最少数量
for(int k=1; min_num>0; k<<=1) //使用二进制优化
{
if(k>min_num) //如果最后不够k个了,就直接赋值给k
k=min_num;
min_num-=k;
for(int j=m; j>=a[i]*k; j--)
{
dp[j]=max(dp[j], dp[j-a[i]*k]+a[i]*k);
}
}
}
int ans=0;
for(int i=1; i<=m; i++)//这里遍历一遍dp里面dp[i]==i的个数
if(dp[i]==i)
ans++;
printf("%d\n", ans); //输出个数就是答案
}
return 0;
}
欢迎评论交流!