题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2191
题意:
C组测试数据
每组输入n,m,n:总经费 m:大米种类
输入每种大米的价格,重量,袋数(即库存)
问:最多能买多少大米(输出重量)
解题思路:
将多重背包转化为01背包,就是假设每一种米都只有一袋,那结果就只有两种:取或不取
很明显,直接可以用01背包解决
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
struct node
{
int v,w;
}p[2005];
int ans[105];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,pi,h,c,l=0;
memset(p,0,sizeof(p));
memset(ans,0,sizeof(ans));
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&pi,&h,&c);
while(c--)
{
p[l].v=pi;
p[l].w=h;
l++;
}
}
for(int i=0;i<l;i++)
{
for(int j=n;j>=p[i].v;j--)
{
ans[j] = max(ans[j],ans[j-p[i].v]+p[i].w);
}
}
printf("%d\n",ans[n]);
}
return 0;
}
然而,当数据特别大时,用这种方法会超时
可以用二进制的那种多重背包
二进制的多重背包实际上也还是转化为01背包和完全背包,只不过用了二进制,大大缩短了时间
首先要弄明白一点:
对于一个数n来说,他可以被拆成 n = a1*2^0+a2*2^1+a3*2^2.....+ak*2^(k-1)+res
=a + b + c +d + .... + e + res
其中a1,a2,a3……的值是0,1,res是剩余的不能转化为2的k次方的数
这样表示的好处就是,可以用a b c d e res表示n以内的任意数
比如n=13,n = 1+2+4+6
1 = 1
2 = 2
3 = 1+2
4 = 4
5 = 1+4
6 = 2+4
7 = 1+6等等
多重背包总会涉及到一个数量的问题,同样的,我们可以将数量用2的k次方表示
回归本题
对每一种米都枚举过去
如果这种米的 数量x单价>n,就说明拥有经费n时,想买这种米与它的数量无关,可以用完全背包,因为即使花掉所有的经费,你也不能买下这种所有的米
否则就用01背包
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
#define N 105
int n,m;
struct Rice
{
int price;
int weight;
int number;
}rice[N];
int dp[N];
void CompletePack(int cost,int weight)//完全背包
{
for(int i=cost;i<=n;i++)
{
dp[i]=max(dp[i],dp[i-cost]+weight);
}
}
void ZeroOnePack(int cost,int weight)//01背包
{
for(int i=n;i-cost>=0;i--)
{
dp[i]=max(dp[i],dp[i-cost]+weight);
}
}
void MultiplePack(int cost,int weight,int number)//多重背包
{
//如果大于等于金额,就按完全背包处理(此时相当于不限定袋数)
if(cost*number>=n)
{
CompletePack(cost,weight);
return ;
}
int k=1;
while(k<number)
{
ZeroOnePack(k*cost,k*weight);//注意要*k
number-=k;
k*=2;
}
ZeroOnePack(number*cost,number*weight);
}
int main()
{
int _case;
scanf("%d",&_case);
while(_case--)
{
scanf("%d%d",&n,&m);
memset(dp,0,sizeof(dp));
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&rice[i].price,&rice[i].weight,&rice[i].number);
}
for(int i=0;i<m;i++)
{
MultiplePack(rice[i].price,rice[i].weight,rice[i].number);
}
printf("%d\n",dp[n]);
}
return 0;
}