题目
Problem Description
急!灾区的食物依然短缺!
为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品,其价格不等,并且只能整袋购买。
请问:你用有限的资金最多能采购多少公斤粮食呢?
Input
输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。
Output
对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。每个实例的输出占一行。
Sample Input
1
8 2
2 100 4
4 100 2
Sample Output
400
分析
根据题目输入可知,每次都还要输入大米的袋数,所以每个种类的大米也许不止一袋,所以这道问题就是一个多重背包问题(0-1背包问题的扩展)。
由0-1背包问题的思路,我们可能会想到,就算它每种大米有好多袋,我把这些这些大米都看成一袋一袋的,只不过他们的重量和价值均相同,然后再用0-1背包问题的解法求解,不就行了吗?其实确实是可以这样子做的,但是这种做法效率会很低,因为可能有好多好多袋大米,那我们要怎样改进呢?
我们可以把相同种类的大米进行组合,比如我们有A类大米6袋,我们可以分成1袋,2袋,3袋这三组,每组的重量和价值都是那几袋大米的重量之和和价值之和,然后把这些组合过的大米当成一个单独的商品,其他种类的大米也是这样子。最后,就可以对这些组合过的大米,用0-1背包问题进行解决,这样子效率就会高很多。
但是还有一个问题,我们应该怎么对相同种类的大米进行组合呢?比如还是A类大米6袋,我们把它分成1,2,3三种组合,那如果我们想要A类大米4袋,但是我们没有这样子分啊,那怎么办呢?也就是说,我们应该怎样组合呢?
这就是一个数学问题,假设某类大米有K袋,则K可以表示为K = 2^0 + 2^1 + 2^2 + … + 2^c-1 + (k-2^c + 1);比如某类大米有5袋,则5 = 1+2+2,那么就可以把5袋大米分成1袋,2袋,2袋。那如果我们想要1袋大米,或者2袋大米,那么我们都有。如果想要三袋大米,那么我们可以用分好的1袋和2袋再组合成三袋。如果想要四袋大米,那么我们可以用分好的2袋和2袋再组合成4袋。如果我们想要5袋,那么全部选了就行了。所以,按照上述公式进行分,那么从1到K的任何袋大米我们都可以选出来了。
代码
#include <iostream>
#include <algorithm>
using namespace std;
int weight[210];
int value[210];
int amount[20];
int newWeight[2010];
int newValue[2010];
int dp[110];
int main()
{
int caseNumber;
scanf("%d",&caseNumber);
while(caseNumber--){
int n,m;
//经费金额m 大米种类n
scanf("%d%d",&m,&n);
//拆分成组,number表示第number组
int number = 0;
for(int i = 1; i <= n; i++){
//注意,大米的价格相当于0-1背包问题的重量
//大米的重量相当于0-1背包问题的价格
//因为我们是要花一定的钱买最多的米
//0-1背包问题是一定的容量装最多的重量
scanf("%d%d%d",&weight[i],&value[i],&amount[i]);
//先按2的j次幂开始拆分
for(int j = 1; j <= amount[i]; j*=2){
number++;
//拆分后形成的第number组的重量
newWeight[number] = weight[i] * j;
//拆分后形成的第number组的价值
newValue[number] = value[i] * j;
amount[i] -= j;
}
//按2的k次幂拆分后剩下的无法拆分的则组成一组
if(amount[i] > 0){
number++;
newWeight[number] = weight[i] * amount[i];
newValue[number] = value[i] * amount[i];
}
}
//初始化dp数组
for(int j = 0; j <= m; j++){
dp[j] = 0;
}
for(int i = 1; i<=number; i++){
for(int j = m; j >=newWeight[i]; j--){
dp[j] = max(dp[j],dp[j-newWeight[i]] + newValue[i]);
}
}
printf("%d\n",dp[m]);
}
return 0;
}