【codevs5226】物品选取

该博客讨论了一种0/1背包问题,其中包含三种不同属性的物品。在背包容量不超过m的情况下,目标是最大化物品的总价值。通过动态规划O(n*m^2)的算法来解决,定义状态函数f(j)表示前i个物品中选择若干件,使得物品体积不超过j时的最大价值。博客中详细解释了如何处理不同类型的物品,并给出了代码实现的注意事项,特别是处理多类物品和循环j的重要性。最终,解决方案是找到最大价值的f(m)。
摘要由CSDN通过智能技术生成
【问题描述】
  
 
  小沐同学确信所有问题都有个多项式时间算法,为了证明,他决定自己去当一次旅行商,在上路之前,小 X 需要挑选一些在路上使用的物品,但他只有一个能装体积为 m 的背包。显然,背包问题对小沐来说过于简单了,所以他希望你来帮他解决这个问题。

  小沐可以选择的物品有 n样,一共分为甲乙丙三类:
  1.甲类物品的价值随着你分配给他的背包体积变化,它的价值与分配给它的体积满足函数关系式,v(x) = A*x^2-B*x,x表示分配给该物品的体积,为非负整数,A,B是每个甲类物品的两个参数。注意每个体积的甲类物品只有一个。
  2.乙类物品的价值 A和体积 B都是固定的,但是每个乙类物品都有个参数C,表示这个物品可供选择的个数。
  3.丙类物品的价值 A和体积 B也是固定的,但是每个丙类物品可供选择的个数都是无限多个。

  你最终的任务是确定小沐的背包最多能装有多大的价值上路。
    
  【输入格式】 
 
  第一行两个整数 n,m,表示背包物品的个数和背包的体积;
  接下来 n行,每行描述一个物品的信息。第一个整数 x,表示物品的种类:
  若 x 为1表示甲类物品,接下来两个整数 A,B,为A类物品的两个参数;
  若 x 为2表示乙类物品,接下来三个整数 A,B,C。A表示物品的价值,B表示它的体积,C 表示它的个数;
  若 x 为3表示丙类物品,接下来两个整数A,B。A表示它的价值,B表示它的体积。
    
  【输出格式】 
 
  仅一行为一个整数,表示小 X的背包能装的最大价值。
    
  【输入样例】 
 
【样例1】
 1 0
 1 1 1

【样例2】
 4 10
 2 1 2 1
 1 1 2
 3 5 2
 2 200 2 3
    
  【输出样例】 
 
【样例1】
 0

【样例2】
 610
    
  【数据范围】 
 
对于50%的数据,只有乙和丙两类物品;
对于70%的数据,1<=n<=100, 1<=m<=500,0<=A,B,C<=200;
对于100%的数据,1<=n<=100, 1<=m<=2000,0<=A,B,C<=200;

题目大意:有三种物品,每种物品有着自己不同的属性,现在求在最后装载的总体积小于等于m的前提下,背包内物品的最大价值。这类背包问题关心的是"选不选"这个问题。是标准的0/1背包。

算法:动态规划O(n*m^2)

设状态函数f(j)表示在前i个物品中选择一些装入背包,在背包内物品体积和不超过j的前提下,背包内物品价值的最大值。

先考虑较为简单的乙类和丙类物品,对于乙类物品i,每一次都可以选0件,选1件...直到选min(a[i].C,j/a[i].B)件。对应状态f(j-a[i].B)+a[i].A,f(j-2*a[i].B)+2*a[i].A...f(j-min(j/a[i].B,a[i].C)

而丙类物品和乙类物品类似,只是没有a[i].c的限制。

现在考虑最难的甲类物品,甲类物品的价值随着指定体积的大小而变化。而当前可指定给甲物品的体积为0(不放)、1......、j

对应状态f(j)、f(j-1)+A*1^2+B*1、.......、f(0)+A*j^2+B*j。

在三类物品的所有状态中,取一个最大的价值,就是所求的f(i,j)

代码实现时注意解这种物品种类和属性较多的题,要注意分类处理,每考虑一个物品,都应该取循环一次j。

最后的答案f(m)

边界 f(0)=0(不能放任何物品)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#define maxn 105
#define maxm 2005
using namespace std;
typedef long long LL;
int n,m;
struct data
{
	int A,B,C,x;
}a[maxn];
LL f[maxm];
int main()
{
	//freopen("select.in","r",stdin);
	//freopen("select.out","w",stdout);
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		if(x==1)
		{
			int A,B;
			scanf("%d%d",&A,&B);
			a[i]=(data){A,B,1,x};
		}
		
		if(x==2)
		{
			int A,B,C;
			scanf("%d%d%d",&A,&B,&C);
			a[i]=(data){A,B,C,x};
		}
		
		if(x==3)
		{
			int A,B;
			scanf("%d%d",&A,&B);
			a[i]=(data){A,B,2005,x};
		}
		
	}	
	
	f[0]=0;
	
	for(int i=1;i<=n;i++)
	{
		if(a[i].x==1)
		{
			for(int j=m;j>=0;j--)
			for(int x=j;x>=1;x--)
			f[j]=max(f[j],f[j-x]+a[i].A*x*x-a[i].B*x);
		}
		
		if(a[i].x==2)
		{
			for(int j=m;j>=0;j--)
			for(int x=1;x<=a[i].C && x*a[i].B<=j;x++)
			f[j]=max(f[j],f[j-x*a[i].B]+x*a[i].A);
		}
		
		if(a[i].x==3)
		{
			for(int j=a[i].B;j<=m;j++)
			f[j]=max(f[j],f[j-a[i].B]+a[i].A);
		}
		
	}
	
	cout<<f[m]<<'\n';
	
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值