题目描述

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过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. 腾讯装扮下拉选项卡特效(QQ空间)

    <DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" ...

  2. 剑指Offer - 九度1356 - 孩子们的游戏(圆圈中最后剩下的数)

    剑指Offer - 九度1356 - 孩子们的游戏(圆圈中最后剩下的数)2014-02-05 19:37 题目描述: 每年六一儿童节,JOBDU都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.H ...

  3. 编译gearman提示缺少boost

    编译german时提示缺少boost: checking for boostlib >= 1.39... configure: We could not detect the boost lib ...

  4. loadrunner检查点设置失败,日志中SaveCount无法被正常统计出来

    在脚本正确的情况下的web_reg_find检查点检查失败,SaveCount无法被正常统计出来. 在检查项Text为中文的情况下, ******(我是被录制下来的代码) web_reg_find(& ...

  5. python+selenium安装指导

    一.      安装python 1.Window 平台安装 Python 以下为在 Window 平台上安装 Python 的简单步骤: 打开 WEB 浏览器访问https://www.python ...

  6. Python网络编程(子进程的创建与处理、简单群聊工具)

    前言: 昨天我们已经了解了多进程的原理以及它的实际使用 Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊.普通的函数调用,调用一次,返回一次, 但是fork()调用一次,返回两次 ...

  7. Python——数据类型之str

    本篇主要内容: 1.str元素的访问 2.str内置43个方法使用示例 str就是储存各种东西的字符串. 他的方法也是最多的...有各种各样的神奇魔法. 1.str的访问,str可以想列表那样访问每一 ...

  8. NodeJs06 高并发

    高并发架构 在业务的最初期,由于业务和用户的体量比较小,可能采用单机就足够了.随着业务的增长,用户量和并发请求量都会不断上升.当增长到一定的瓶颈的时候,系统能否抗住压力,就需要采取一些方案了.这就是著 ...

  9. 通过nbviwer在线分享python notebook

    在数据科学计算中,jupyter-notebook是一个很得力的助手,但是Notebook写完之后如何与他人分享呢?我们可以使用nbviwer. 具体思路: 具体的方法如下: 本地编写ipython ...

  10. web自动化测试:watir+minitest(五)

    测试报告: 加载minitest-reporters库,并设置相关的参数.既可以在每次运行测试后生成响应的测试报告. 默认会生成一份html的报告在当前目录的test目录下 我们可以指定参数对报告的标 ...