您的位置:首页 > 其它

HDOJ2844-Coins(DP)

2015-05-05 20:39 204 查看
题意:你有n种面值的硬币,告诉你每种硬币的数量,问这些硬币能拼成多少不超过m的面额。

思路:多重背包。当某种硬币的总额大于背包容量时进行完全背包,否则进行二进制拆分进行01背包。最后统计f[i]==i的数量,即恰好能凑出金额为i的情况总数。详解请参考背包九讲。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int f[100010];
int v[105],s[105];//面值,数量

int max(int x,int y)
{
    return x>y?x:y;
}

void work(int n,int m)
{
    int i,j,k,t,ans;
    memset(f,0,sizeof(f));

    for(i=0;i<n;i++) scanf("%d",&v[i]);
    for(i=0;i<n;i++) scanf("%d",&s[i]);

    for(i=0;i<n;i++)
    {
        if(v[i]*s[i]>=m)
        {
            //若某种金币的总金额大于总价,则直接进行完全背包
            for(j=v[i];j<=m;j++)
                f[j]=max(f[j],f[j-v[i]]+v[i]);
        }
        else
        {
            k=1;
            while(k<s[i])
            {
                //将物品进行二进制拆分,进行01背包操作
                for(j=m;j>=v[i]*k;j--)
                    f[j]=max(f[j],f[j-v[i]*k]+v[i]*k);
                s[i]-=k;
                k<<=1;
            }
            //对二进制拆分后剩下的物品进行01背包
            for(j=m;j>=v[i]*s[i];j--)
                f[j]=max(f[j],f[j-v[i]*s[i]]+v[i]*s[i]);
        }
    }

    ans=0;
    for(i=1;i<=m;i++)
        ans+=(f[i]==i);
    printf("%d\n",ans);
}

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m),n||m)
        work(n,m);
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: