HDU 2844枚のコイン、複数のバックパック
問題の意味
Nコインの種類は、各コインの量があり、与える(あい\)\、の番号\(Ciは\で)、その後、あなたの範囲を与えるには、\(M \)は、組成物の要件、これらの金額を形成することができます使用するどのように多くのコインを尋ね量\((1、M)\ ) の範囲内です。
問題解決のためのアイデア
この問題を解決するために、複数のバックパックを使用してください。だから我々は、お金のほとんどの量の下でコインの枚数で構成(バックパック容量として見ることができる)の各量を得ることができること。二つの重バックパックの後、次いでループに使用しては、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;
}