题目描述话说发源于小朋友精心设计的游戏被电脑组的童鞋们藐杀之后非常不爽,为了表示安慰和鼓励,VIP999决定请他吃一次“年年大丰收”,为了表示诚意,他还决定亲自去钓鱼,但是,因为还要准备2013NOIP,z老师只给了他H(1<=H<=16)个小时的空余时间,假设有N(2<=n<=25)个鱼塘都在一条水平路边,从左边到右编号为1、2、3、。。。、n)。VIP是个很讲究效率的孩子,他希望用这些时间钓到尽量多的鱼。他从湖1出发,向右走,有选择的在一些湖边停留一定的时间钓鱼,最后在某一个湖边结束钓鱼。他测出从第I个湖到I+1个湖需要走5*ti分钟的路,还测出在第I个湖边停留,第一个5分钟可以钓到鱼fi,以后再每钓5分钟鱼,鱼量减少di。为了简化问题,他假定没有其他人钓鱼,也不会有其他因素影响他钓到期望数量的鱼。请编程求出能钓最多鱼的数量。输入输出格式输入格式:
第一行:湖的数量n。第二行:时间h(小时)。第三行:n个数,f1,f2,…fn。第四行:n个数,d1,d2,….dn。第五行:n-1个数,t1,t2,….tn-1输出格式:
一个数,所能钓鱼的最大数量。输入输出样例输入样例#1: 复制
2
1
10 1
2 5
2
输出样例#1: 复制
31
昨天想到的写法是,在右边的池塘钓鱼后返回左边钓到的鱼就等价于从左边到达右边,则路上耗时的最小值就是t[最远到达的点]*5,再用合并果子的思想每次取出最大的一个,减去d[堆顶元素]再重新维护一次堆。
只能过样例,也想通为什么了。
极限思想:假设很远很远的地方有一个最大的鱼塘,而很近的地方有一个比它略小但是仍然很大的鱼塘,显然跑到后面钓鱼不合适,废。
正解是用枚举构造条件(最远到达点)辅助堆进行选择,n次选择后就可以了。
几个细节是
- 关于堆中元素的序号,可以用一个数组ids存,维护堆的时候顺便swap了ids
2. 在维护堆的时候,break条件应该或一个(hp[next] == hp[now]&&d[next]>d[now])
,因为当两个鱼塘都有一样的鱼时(在枚举的范围内),显然应该选择减少较慢的鱼塘。
第二个不加也行,本质上先钓哪个都一样
堆的应用!
#include<iostream>
#define MAXN 2000
using namespace std;int n,h,oh;int f[MAXN],d[MAXN],t[MAXN];int ids[MAXN];
int hp[MAXN];
int size,mxsz;int ans,mx;void reb(int x) {int now=x,next;while(now<<1<=mxsz) {next=now<<1;if(next<mxsz&&hp[next] <hp[next+1] ) next++;if(hp[next] <hp[now] ||(hp[next] == hp[now]&&d[next]==d[now]) ) break;swap(hp[next],hp[now]);swap(ids[next],ids[now]);now=next;}
}int main() {cin>>n>>oh;oh*=60;int i;for(i=1; i<=n; i++) cin>>f[i];for(i=1; i<=n; i++) cin>>d[i];for(i=2; i<=n; i++) cin>>t[i],t[i]+=t[i-1];for(i=1; i<=n; i++) {h=oh;for(int j=1; j<=i; j++) {hp[j]=f[j];ids[j]=j;}mxsz=i;for(int j=1; j<=i>>1; j++) reb(j);ans=0;while(h-5-5*t[i]>=0) {//5*!h-=5;ans+=hp[1];hp[1]-=d[ids[1]];reb(1);if(hp[1]<=0) break;}mx=max(mx,ans);}cout<<mx<<endl;return 0;
}