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

时间:2021-06-14 21:40:10

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