珍惜现在,感恩生活(HDU-2191)

题目

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值