《逃亡的准备》 题解

题目描述

题目描述

在《Harry Potter and the Deathly Hallows》中,Harry Potter他们一起逃亡。

现在有许多的东西要放到赫敏的包里面,但是包的大小有限,所以我们只能够在里面放入非常重要的物品,现在给出该种物品的数量、体积、价值的数值,希望你能够算出怎样能使背包的价值最大的组合方式,并且输出这个数值,赫敏会非常地感谢你。

输入格式

(1)第一行有2个整数,物品种数n和背包装载体积v。

(2)2行到n+1行每行3个整数,为第i种物品的数量m、体积w、价值s。.

输出格式

仅包含一个整数,即为能拿到的最大的物品价值总和。

样例数据

input

2 10                            
3 4 3
2 2 5

output

13

【注释】

选第一种一个,第二种两个。

结果为3∗1+5∗2=13

数据规模与约定

对于100%的数据

1<=v<=5000

1<=n<=5000

1<=m<=5000

1<=w<=5000



题解

        与01背包和完全背包不同,多重背包的物品是有限个,将物品放入背包时既要考虑数量,又要考虑体积,同时还要满足最大价值

        对于第i种物品有a[i]+1种取用方案,取0--a[i]件,这样第i种就转换成了a[i]个物品

        \bullet如果物品足够多,没等物品用完就把背包装满了,所以可看作物品无限的完全背包问题

        \bullet如果物品不够多,需要对物品数量进行拆分,为了提高效率,可以采用二进制的思想进行拆分(Eg:数量为14的物品可拆为1,2,4,7,用这4个数字就可以组成1--14所有数量),将每种数量与其体积,价值相乘,转化为重量为k*w[i],价值为k*v[i]的物品的01背包问题,二进制拆分完后再把剩下的那个数字(如例子中的7)作为单独的一个系数作为一个系数作01背包

一、阶段划分

        参考背包问题,用物品作阶段

二、状态表达

        将数量拆分后当作整体的重量和体积作01背包,所以将数量放入循环中,所以仍然采用一维数组记录体积

 三、初始状态

        背包不需要一定装满,所以初值为0

四、状态转移

        按上述题解,依据不同物品的总体积与背包体积的关系分两种情况,分别作01背包和完全背包

        1、物品足够多\Rightarrow完全背包

	if(num[i]*volu[i]>=maxx)//如果物品足够多,看做完全背包
		{
			for(int j=volu[i];j<=maxx;j++)
			{
				f[j]=max(f[j],f[j-volu[i]]+val[i]);	
			}	
		}	

        2、物品不够多\Rightarrow01背包 

else//完全背包二进制拆分
		{
			int pp=1,amount=num[i];//pp代表2的整次幂,amount表示拆分物品剩余系数
			while(pp<amount)
			{
			    for(int j=maxx;j>=pp*volu[i];j--)//重量为pp*volu[i],价值为k*val[i]的物品的01背包
				{
					f[j]=max(f[j],f[j-volu[i]*pp]+pp*val[i]);
				}
				amount-=pp;
				pp+=pp;
			}
			for(int j=maxx;j>=amount*volu[i];j--)//把剩下的数字作为单独一个系数再做01背包
			{
				f[j]=max(f[j],f[j-amount*volu[i]]+amount*val[i]);
			}
		}

五、最终答案

         同样将最优值赋到最大体积上f[maxx]

六、总结

        注意不同情况的不同背包问题,物品足够多的时候为完全背包物品不够多的时候为01背包

七、核心DP代码实现

void bag_dp()
{
	for(int i=1;i<=n;i++)
	{
		if(num[i]*volu[i]>=maxx)
		{
			for(int j=volu[i];j<=maxx;j++)
			{
				f[j]=max(f[j],f[j-volu[i]]+val[i]);	
			}	
		}	
		else
		{
			int pp=1,amount=num[i];
			while(pp<amount)
			{
				for(int j=maxx;j>=pp*volu[i];j--)
				{
					f[j]=max(f[j],f[j-volu[i]*pp]+pp*val[i]);
				}
				amount-=pp;
				pp+=pp;
			}
			for(int j=maxx;j>=amount*volu[i];j--)
			{
				f[j]=max(f[j],f[j-amount*volu[i]]+amount*val[i]);
			}
		}
		
	} 
	cout<<f[maxx];
}

八、本题代码

#include<bits/stdc++.h>
using namespace std;
int n,maxx;
int num[10000],volu[10000],val[10000];
int f[1000000];
void init()
{
	cin>>n>>maxx;
	for(int i=1;i<=n;i++)
		cin>>num[i]>>volu[i]>>val[i];
}
void bag_dp()
{
	for(int i=1;i<=n;i++)
	{
		if(num[i]*volu[i]>=maxx)
		{
			for(int j=volu[i];j<=maxx;j++)
			{
				f[j]=max(f[j],f[j-volu[i]]+val[i]);	
			}	
		}	
		else
		{
			int pp=1,amount=num[i];
			while(pp<amount)
			{
				for(int j=maxx;j>=pp*volu[i];j--)
				{
					f[j]=max(f[j],f[j-volu[i]*pp]+pp*val[i]);
				}
				amount-=pp;
				pp+=pp;
			}
			for(int j=maxx;j>=amount*volu[i];j--)
			{
				f[j]=max(f[j],f[j-amount*volu[i]]+amount*val[i]);
			}
		}
		
	} 
	cout<<f[maxx];
}
int main()
{
	init();
	bag_dp();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值