HDU 2191悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包)

HDU 2191悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包)

http://acm.hdu.edu.cn/showproblem.php?pid=2191

题意:

       假设你有资金n元, 然后有m种大米, 每种大米价格为cost[i], 重量为val[i], 数量为num[i]. 现在问你用n元钱最多能买多重的大米?

分析:

       本题是典型的多重背包问题.

       我们令dp[i][j]==x表示只购买前i种大米, 且总费用<=j时能购买的大米最大重量为x.

       初始化: dp为全0.

       由于每种大米有数量num[i], 所以我们分下面两种情况做:

       cost[i]*num[i]>=n, 我们直接对该种大米做一次完全背包过程即可.

       cost[i]*num[i]<n , 我们把num[i]个第i类大米看成下面k+1种物品:

       1个(i类物品)  2个 4个 2^(k-1)个 以及 num[i]-2^k+1个

       我们对上述k+1种新物品每个都做一个01背包即可覆盖我们可能对第i种物品做出的所有选择.

       最终所求: dp[m][n]的值.

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=4000+5;

int n;//金额
int m;//大米种类
int cost[100+5];//价格
int val[100+5]; //重量
int num[100+5]; //数量
int dp[maxn];

//一次01背包
void ZERO_ONE_PACK(int cost,int val)
{
    for(int i=n;i>=cost;i--)
        dp[i] = max(dp[i], dp[i-cost]+val);
}

//一次完全背包
void COMPLETE_PACK(int cost,int val)
{
    for(int i=cost;i<=n;i++)
        dp[i] = max(dp[i], dp[i-cost]+val);
}

//一次多重背包
void MULTIPLE_PACK(int cost,int val,int num)
{
    if(cost*num>=n)
    {
        COMPLETE_PACK(cost,val);
        return;
    }

    int k=1;
    while(k<num)
    {
        ZERO_ONE_PACK(k*cost,k*val);
        num -=k;
        k*=2;
    }
    ZERO_ONE_PACK(num*cost,num*val);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        //读取输入
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&cost[i],&val[i],&num[i]);

        //初始化+递推
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=m;i++)
            MULTIPLE_PACK(cost[i],val[i],num[i]);

        //输出结果
        printf("%d\n",dp[n]);
    }
    return 0;
}

你可能感兴趣的:(Algorithm,算法,dp,ACM)