HDU 2844 Coins 背包问题 + 二进制优化

题目大意:某个人有n种硬币,每种硬币价值为v,数量为c,问在总价值不超过m的条件下,最多有多少种组合方式。

题目思路:

1.对于某种硬币 如果v*c 大于 m,就意味着无论取多少枚硬币,只要总价值不大于m就取不完该种硬币--完全背包。

2.如果某种硬币,如果v*c 不大于m,就意味着这是多重背包,因此可以用二进制方法优化一下。

3.对于不大于m的任意数字j,dp[j]=0代表无法组合成j,dp[j]=1 代表可以组合成j,dp[j]=dp[j]|dp[j-v[i]*k](k为选择第i种硬币的数量)。

二进制优化:1,2,4,8……可以组合成任意数,将十进制数字1,2,4,8……转化成二进制

1

10

11

100

……

我们可以发现1,2,4,8……等数字的二进制每一位上都可以组合形成0或1,因此可以组合成其他任意数字。

#include<cstdio>
#include<stdio.h>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
#include<queue>
#define INF 0x3f3f3f3f
#define MAX 1000005

using namespace std;

int dp[MAX],v[MAX],c[MAX],n,m;

int main()
{
    int i,j,k;
    while(scanf("%d%d",&n,&m),n+m)
    {
        for(i=1;i<=n;i++)
            scanf("%d",&v[i]);
        for(i=1;i<=n;i++)
            scanf("%d",&c[i]);
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        for(i=1;i<=n;i++)
        {
            if(c[i]*v[i] >= m)
            {
                for(j=v[i];j<=m;j++)//完全背包
                    dp[j]=dp[j]|dp[j-v[i]];
            }

            else
            { 
                for(k=1;k<=c[i]/2;k*=2)//二进制优化
                {
                    for(j=m;j>=v[i]*k;j--)//多重背包
                    {
                        dp[j]=dp[j]|dp[j-v[i]*k];
                    }
                }

                k=c[i]-k+1;
                for(j=m;j>=v[i]*k;j--)
                    dp[j]=dp[j]|dp[j-v[i]*k];
            }
        }
        int ans=0;
        for(i=1;i<=m;i++)
            if(dp[i])
            ans++;
        printf("%d
",ans);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/alan-W/p/5755591.html