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

题目链接: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;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值