2014.10.20
继续复习DP。
Noip2006考了一道《金明的预算方案》,有依赖背包问题,再做做。
描述 Description
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过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]。(其中*为乘号)请你帮助金明设计一个满足要求的购物单。
输入格式 Input Format
输入文件的第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是所属主件的编号)
输出格式 Output Format
输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值
(<200000)。
样例输入 Sample Input
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
样例输出 Sample Output
2200
时间限制 Time Limitation
1s
来源 Source
NOIP2006第二题
分析:标准解法为有依赖性背包问题。
还有一种取巧方法,将每个主件及其附件的所有选择方案分为一个组,然后整体做分组背包,因为这道题的附件最多两个,所以可以这么做,只需预处理出所有组即可。
我写了依赖性背包的解法,主要思想就是对一个主件的所有附件做01背包,求出每个主件的最优解,然后对每个主件做01背包即可。
代码如下:
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
int N,M,a[41000],f[41000];
int v[70],w[70],p[70];
void init()
{
scanf("%d%d",&N,&M);
for(int i=1;i<=M;i++)
{
scanf("%d%d%d",&v[i],&w[i],&p[i]);
w[i]*=v[i];
}
}
void DP()
{
for(int i=1;i<=M;i++)
if(p[i]==0)
{
for(int j=1;j<v[i];j++) a[j]=0;
for(int j=v[i];j<=N;j++) a[j]=f[j-v[i]]+w[i];
for(int j=1;j<=M;j++)
if(p[j]==i)
for(int k=N;k>=v[i]+v[j];k--)
if(a[k-v[j]]+w[j]>a[k]) a[k]=a[k-v[j]]+w[j];
for(int j=v[i];j<=N;j++)
if(a[j]>f[j]) f[j]=a[j];
}
printf("%d\n",f[N]);
}
int main()
{
init();
DP();
return 0;
}
看看分组背包,有一道竞赛真理,比较简单。
描述 Description
牛玉鑫在经历了无数次学科竞赛的失败以后,得到了一个真理:做一题就要对一题!但是要完全正确地做对一题是要花很多时间(包括调试时间),而竞赛的时间有限。所以开始做题之前最好先认真审题,估计一下每一题如果要完全正确地做出来所需要的时间,然后选择一些有把握的题目先做。 当然,如果做完了预先选择的题目之后还有时间,但是这些时间又不足以完全解决一道题目,应该把其他的题目用贪心之类的算法随便做做,争取“骗”一点分数。
问题求解:
根据每一题解题时间的估计值,确定一种做题方案(即哪些题目认真做,哪些题目“骗”分,哪些不做),使能在限定的时间内获得最高的得分,
输入格式 Input Format
根据每一题解题时间的估计值,确定一种做题方案(即哪些题目认真做,哪些题目“骗”分,哪些不做),使能在限定的时间内获得最高的得分,从文件读入数据。第一行有两个正整数N和T,表示题目的总数以及竞赛的时限(单位秒)。以下的N行,每行4个正整数W1i 、T1i 、W2i 、T2i,分别表示第i题:完全正确做出来的得分,完全正确做出来所花费的时间(单位秒),“骗”来的分数,“骗”分所花费的时间(单位秒)。
其中,3≤N≤30,2≤T≤1080000,1≤ W1i 、W2i ≤30000,1≤T1i、T2i≤T。
输出格式 Output Format
直接把所能得到的最高分值输出到文件TRUTH.OUT,文件只有一行(包括换行符)。
输入格式 Input Format
根据每一题解题时间的估计值,确定一种做题方案(即哪些题目认真做,哪些题目“骗”分,哪些不做),使能在限定的时间内获得最高的得分,从文件读入数据。第一行有两个正整数N和T,表示题目的总数以及竞赛的时限(单位秒)。以下的N行,每行4个正整数W1i 、T1i 、W2i 、T2i,分别表示第i题:完全正确做出来的得分,完全正确做出来所花费的时间(单位秒),“骗”来的分数,“骗”分所花费的时间(单位秒)。
其中,3≤N≤30,2≤T≤1080000,1≤ W1i 、W2i ≤30000,1≤T1i、T2i≤T。
输出格式 Output Format
直接把所能得到的最高分值输出到文件TRUTH.OUT,文件只有一行(包括换行符)。
样例输入 Sample Input
样例1:
4 10800
18 3600 3 1800
22 4000 12 3000
28 6000 0 3000
32 8000 24 6000
样例2:
3 7200
50 5400 10 900
50 7200 10 900
50 5400 10 900
样例输出 Sample Output
样例1:
50
样例2:
70
时间限制 Time Limitation
1s
分析:每组中物品很少,只要分情况讨论即可(优先选择认真做的)。
代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int n,T,f[1080005];
void init()
{
memset(f,0,sizeof(f));
cin>>n>>T;
}
void work()
{
for(;n>0;n--)
{
int w1,t1,w2,t2;
cin>>w1>>t1>>w2>>t2;
for(int j=T;j>=0;j--)
{
if(j>=t1) f[j]=max(f[j],f[j-t1]+w1);//优先选择得全分
if(j>=t2) f[j]=max(f[j],f[j-t2]+w2);
}
}
cout<<f[T]<<endl;
}
int main()
{
ios::sync_with_stdio(false);
init();
work();
return 0;
}