您的位置:首页 > 其它

hdu 2844 coins (多重背包+二进制优化)

2015-10-04 14:57 351 查看
给出n种面值的硬币,以及它们的数量,问能组成不超过m的面值有多少个。

样例2解释:

2 5
1 4 2 1

面值为1的硬币2个,面值为4的硬币1个。能组成的面值有1、2、4、5、6。其中不超过面值5的面值有4个。

典型的多重背包问题。

这里的面值既相当于背包问题中的物品体积,又相当于物品的价值。

因此dp[i]表示的是能组成的不超过i的最大面值,即dp[i]<=i,对于某种面值k,若i>k且dp[i]=k,则之前必然有dp[k]=k。

最终统计答案的时候,从1到m扫一遍,统计dp[i]=i的面值i的数量即可,这样可保证能组成的各种面值刚好只被统计一次。

关于二进制优化,其实就是利用二进制的原理对同种的物品的数量进行划分。将多个同种物品视为一个物品,其价值为原价值乘上划分的数量,体积为原体积乘上划分的数量。

然后将问题转化为0-1背包求解。相比于直接转换为0-1背包,时间复杂度由O(V*sum)优化到O(V*(log(sum)) 其中sum为所有物品的总数量。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 101
#define maxV 100005
int v[maxn],num[maxn];
int dp[maxV];
inline void zero(int vol,int val,int W)
{
for(int i=W;i>=vol;--i)
dp[i]=max(dp[i],dp[i-vol]+val);
}

inline void complet(int vol,int val,int W)
{
for(int i=vol;i<=W;++i)
dp[i]=max(dp[i],dp[i-vol]+val);
}

void multi(int vol,int val,int num,int W)
{
if(vol*num>=W){
complet(vol,val,W);
return;
}
for(int i=1;i<=num;i<<=1){
zero(i*vol,i*val,W);
num-=i;
}
if(num) zero(num*vol,num*val,W);
}

int main()
{
int n,W,i;
while(scanf("%d%d",&n,&W)!=EOF&&(n+W))
{
memset(dp,0,sizeof(dp));
for(i=0;i<n;++i) scanf("%d",&v[i]);
for(i=0;i<n;++i) scanf("%d",&num[i]);
for(i=0;i<n;++i) multi(v[i],v[i],num[i],W);

int ans=0;
for(i=1;i<=W;++i)
if(dp[i]==i) ++ans;
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: