题目描述

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

主件 附件

电脑 打印机,扫描仪

书柜 图书

书桌 台灯,文具

工作椅 无

如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有0个、1个或2个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是10元的整数倍)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:

v[j1]w[j1]+v[j2]w[j2]+ …+v[jk]w[jk]。(其中为乘号)

请你帮助金明设计一个满足要求的购物单。

输入输出格式

输入格式:

输入的第1行,为两个正整数,用一个空格隔开:

N m (其中N(<32000)表示总钱数,m(<60)为希望购买物品的个数。)

从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有3个非负整数

v p q (其中v表示该物品的价格(v<10000),p表示该物品的重要度(1~5),q表示该物品是主件还是附件。如果q=0,表示该物品为主件,如果q>0,表示该物品为附件,q是所属主件的编号)

输出格式:

输出只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<200000)。

输入输出样例

输入样例#1:

1000 5

800 2 0

400 5 1

300 5 1

400 3 0

500 2 0

输出样例#1:

2200

说明

NOIP 2006 提高组 第二题

【分析】:

基本思路

这是一个 有依赖(?) 的01背包

既然物品分为主件和附件两类,且每个主件最多包含两个附件,那么我们不妨枚举所有的主件。那么,对于每次枚举,会有五种情况:

什么都不买

只买主件

买主件和第一个附件

买主件和第二个附件

买主件和两个附件

只要把这四种情况最终的价值算出来,取最大值就可以了。

#include <iostream>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 32005;
const int N = 105;
#define ll long long int n, m;
int a, b, c;
int v[N],q[N],p[N], v1[N],q1[N],v2[N],q2[N],dp[maxn]; int main()
{
while(cin >> n >> m){
for(int i=1; i<=m; i++){
cin >> a >> b >> c;
if(c==0){
v[i]=a; q[i]=b;
}
else{
if(q1[c]==0){
v1[c]=a;q1[c]=b;}
else{
v2[c]=a; q2[c]=b;}
}
}
for(int i=1; i<=m; i++){
for(int j=n; j>=v[i]; j--){
if(j-v[i]>=0){
dp[j]=max(dp[j], dp[j-v[i]] + v[i]*q[i]);
}
if(j-v[i]-v1[i]>=0){
dp[j]=max(dp[j], dp[j-v[i]-v1[i]] + v[i]*q[i] + v1[i]*q1[i]);
}
if(j-v[i]-v2[i]>=0){
dp[j]=max(dp[j], dp[j-v[i]-v2[i]] + v[i]*q[i] + v2[i]*q2[i]);
}
if(j-v[i]-v1[i]-v2[i]>=0){
dp[j]=max(dp[j], dp[j-v[i]-v1[i]-v2[i]] + v[i]*q[i] + v1[i]*q1[i] + v2[i]*q2[i]);
}
}
}
cout<<dp[n]<<endl;
}
}
此题是01背包问题的变形。物品的重要度乘以价格是背包问题中的价值,物品的价格是背包问题中的体积。

1、我们可以把如何在众多主件与附件之中选择购买的问题转变为看成购买的5种方案:

(1)什么都不买,(2)只买主件,(3)买主件和附件1,(4)买主件和附件2,(5)买主件和两个附件。

2、有些主件有附件,而有些没有,这为我们思考带来了负担,我们完全可以假设任何主件都有两个附件,也就是说如果题目没有给出某个主件的附件的话,我们就假设这个主件的附件是存在的,且价格和重要度都等于0。这个假设首先不会影响到程序的正确性,也不会增加多少运算时间,且这种假设使得我们想问题和写程序都变得简单多了。

3、题目中的价格都是10的这个条件,可以减少一些时间和空间的开销。

此题和01背包问题有2个主要的区别:

区别一:01背包问题对当前物品考虑的只有买和不买两种情况,而此题需要考虑上面所说的5种不同的购买方案。

区别二:01背包问题是用v[i]来保存第i个物品的价值,而此题需要用v[i]来保存第i个物品和它的两个附件的价值,此时我们需要二维数组来实现,物品体积w同样需要用二维数组来保存。

v[i][0]表示第i个物品的主件价值, v[i][1]表示第i个物品的第一个附件的价值,v[i][2]表示第i个物品的第二个附件的价值 .w[i][0..2]表示同样的物品的体积。

f[i,j]表示给定i个物品和j的空间能够获得的最大价值总合。

则: f[i,j]=max{f[i-1,j],

f[i-1,j-w[i,0]]+v[i,0],

f[i-1,j-w[i,0]-w[i,1]]+v[i,0]+v[i,1],

f[i-1,j-w[i,0]-w[i,2]]+v[i,0]+v[i,2],

f[i-1,j-w[i,0]-w[i,1]-w[i,2]]+v[i,0]+v[i,1]+v[i,2]}
其实,此题还有一个关键点,就是输入数据的处理。
根据题目的意思,q是物品的编号,但是这个编号是在考虑附件时统计的编号,而我们认为附件和主件是一体的,因此附件编号因该和主件一致,所以我们需要对题目给出的编号进行转换。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn = 40000;
int f[70][maxn];
int value[70][3],imp[70][3];
int main()
{
int n,m;
int v,p,q;
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&v,&p,&q);
//为主件
if (!q)
{
value[i][0] = v;
imp[i][0] = p;
}
//为附件
else
{
if (!value[q][1])
{
value[q][1] = v;
imp[q][1] = p;
}
else
{
value[q][2] = v;
imp[q][2] = p;
}
}
}
memset(f,0,sizeof(f));
for(int i=1; i<=m; i++)
{
for(int j=1; j<=n; j++)
{
if (j-value[i][0]>=0)
{
//仅主件
f[i][j] = max(f[i-1][j],f[i-1][j-value[i][0]] + value[i][0]*imp[i][0]);
//这个时候的f[i][j]表示仅有主件的时候的情况,而下面每种加附件的情况,都是在有主件的基础下,所以
//直接和f[i][j]比较
//主件 + 附件1
if (j-value[i][0]-value[i][1]>=0)
f[i][j] = max(f[i][j],f[i-1][j-value[i][0]-value[i][1]] + value[i][0]*imp[i][0] + value[i][1]*imp[i][1]);
//主件 + 附件2
if (j-value[i][0]-value[i][2]>=0)
f[i][j] = max(f[i][j],f[i-1][j-value[i][0]-value[i][2]] + value[i][0]*imp[i][0] + value[i][2]*imp[i][2]);
//主件 + 所有附件
if (j-value[i][0]-value[i][1]-value[i][2]>=0)
f[i][j] = max(f[i][j],f[i-1][j-value[i][0]-value[i][1]-value[i][2]] + value[i][0]*imp[i][0] + value[i][1]*imp[i][1] + value[i][2]*imp[i][2]);
}
else
f[i][j] = f[i-1][j];
}
}
printf("%d\n",f[m][n]);
return 0;
}
P.S:一开始有个assembler messages的错误,纠结的半天,原来是一开始弄的f[maxn][maxn]数组太大爆炸了,

洛谷 P1064 金明的预算方案【有依赖的分组背包】的更多相关文章

  1. 洛谷 P1064 金明的预算方案 (有依赖的0/1背包)

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NN元钱就行”. ...

  2. 洛谷 P1064 金明的预算方案(有依赖的背包问题)

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”.今 ...

  3. 洛谷P1064 金明的预算方案

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NN元钱就行”. ...

  4. 洛谷 P1064 金明的预算方案

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”.今 ...

  5. [NOIP2006] 提高组 洛谷P1064 金明的预算方案

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”.今 ...

  6. 洛谷P1064 金明的预算方案(01背包)

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NNN元钱就行” ...

  7. Java实现 洛谷 P1064 金明的预算方案

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:"你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NN元 ...

  8. 洛谷 P1064 金明的预算方案【DP/01背包-方案数】

    题目背景 uim神犇拿到了uoi的ra(镭牌)后,立刻拉着基友小A到了一家--餐馆,很低端的那种. uim指着墙上的价目表(太低级了没有菜单),说:"随便点". 题目描述 不过ui ...

  9. 洛谷 P1064 金明的预算方案(01背包问题)

    传送门:Problem 1064 https://www.cnblogs.com/violet-acmer/p/9852294.html 题解: 这道题是 “01”背包问题的变形. 如果不考虑买附件必 ...

随机推荐

  1. Android学习记录(2)—Android中数据库的常见操作

    android中数据库操作是非常常见了,我们会经常用到,操作的方法也有很多种形式,这里我就把最常见的两种形式记录下来了,以备以后用到方便查看.我就不写注释和解释了,因为android数据库的操作和其它 ...

  2. javascript 数组的常用方法总结

    前言 主要讨论一下数组的方法,     1.splice和slice的区别     2.pop和push     3.shift和unshift     4.join     5.forEach(es ...

  3. 最近做group assignment需要些加密的知識

    需求:A給B單向發的數據需要被加密,A和B都可以看到原文.加密后,就算傳輸的過程被竊取,也無法得知數據原文.A可以是任何客戶端. 解決:常用的MD5,sha1等常用的加密算法為單向不可逆,顯然不符合需 ...

  4. gulp批量添加版本号

    要实现的理想效果: "/css/style.css" => "/dist/css/style.css?v=1d87bebe""/js/scrip ...

  5. Java基础-4变量与数据类型

    变量:变量是Java程序中的一个基本存储单元.变量是一个标识符.类型及一个可选初始值的组合定义.所有的变量都有一个作用域,即变量在某一区域有效. 基本的变量声明方式如下: int a; float b ...

  6. Python全栈工程师(异常(高级)、运算符重载)

    ParisGabriel              每天坚持手写  一天一篇  决定坚持几年 为了梦想为了信仰    开局一张图 Python人工智能从入门到精通 对象的属性管理函数: getattr ...

  7. 【转】通过制作Flappy Bird了解Native 2D中的RigidBody2D和Collider

    作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 引子 在第一篇文章[Unity3D基础教程] ...

  8. struts2之文件上传

    一.单文件上传 实例: 表单应该注意三个点   form中的method="post".enctype="multipart/form-data".input中 ...

  9. makefile语法

    makefile很重要 什么是makefile? 或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional 的程 ...

  10. 高级数据查询SQL语法

    接上一篇关系数据库SQL之基本数据查询:子查询.分组查询.模糊查询,主要是关系型数据库基本数据查询.包括子查询.分组查询.聚合函数查询.模糊查询,本文是介绍一下关系型数据库几种高级数据查询SQL语法, ...