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

本文探讨了多重背包问题在解决硬币面额组合问题的应用,详细解释了如何通过二进制优化来提高求解效率。通过实例演示,展示了如何计算不超过特定面额的组合数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给出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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值