题目描述
阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有N家住户,第i家住户到入口的距离为Si米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的X家住户推销产品,然后再原路走出去。
阿明每走1米就会积累1点疲劳值,向第ii家住户推销产品会积累Ai点疲劳值。阿明是工作狂,他想知道,对于不同的X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。
思路分析
一、为何不需要动归
数据范围在十万,如果用动归肯定会爆时空。再说,题目要求输出多个值,从划分阶段的角度来讲就很难划分。既然是最优化问题,不来一发贪心怎能够抒发对动归的憎恨之情呢?
二、怎么贪
从样例可得,推销一家的疲劳值与与两个因素有关:距离Si,积累的疲劳值Ai
具体一点,往返2Si,推销Ai。所以第一步就可以确定下来了:当X=1时,sum=max(2Si+Ai);
接下来,Si与Ai仍然都对ans有关系。分类讨论如下:
记走的最远距离为far
① 如果Si<far,那就可以顺路推销,sum+=A[i];
② 如果Si>far,多走(S[i]-far)*2,即sum+=(S[i]-far)*2;
三、朴素实现
#include<iostream> using namespace std; bool vis[100005]; int a[100005],dis[100005],n,mx,mxdis,far=-1,tempa,tempb,sum; int before(int place) { int bmx=-1,bmxdis=-1; for(int i=1;i<=n;i++) { if(!vis[i]) { if(a[i]>bmx) { bmx=a[i]; bmxdis=i; } } } return bmxdis; } int after(int place) { //place+=1; int amx=-1,amxdis=-1; for(int i=place;i<=n;i++) { if(!vis[i]) { if(a[i]+(dis[i]-place)*2>amx) { amx=a[i]+(dis[i]-place)*2; amxdis=i; } } } return amxdis; } int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>dis[i]; } for(int i=1;i<=n;i++) { cin>>a[i]; } for(int i=1;i<=n;i++) { if(a[i]+dis[i]*2>mx) { mx=a[i]+dis[i]*2; mxdis=i; } } cout<<mx<<endl; vis[mxdis]=1; far=mxdis; sum+=mx; for(int i=2;i<=n;i++) { tempb=before(far); tempa=after(far); if(a[tempb]>a[tempa]+(dis[tempa]-far)*2) { mxdis=tempb; mx=a[tempb]; } else { mxdis=tempa; mx=a[tempa]+(dis[tempa]-far)*2; far=tempa; } vis[mxdis]=1; sum+=mx; cout<<sum<<endl; } return 0; }
但是估计你也看到了,这种算法实在是太慢了,只能得60分(虽然在考场上已经足够了)。
四、优化
对于dalao来说,可以毫不费力地用上logn数据结构,如线段树和树状数组。可是我们这种蒟蒻,想都不要想。
可以看到,每次都会选取far左边和右边能获得的最大疲劳值(计算方式不同)。既然是最大,那么可以使用优先队列(堆?)优化。
但是优先队列不支持随机存取。如果far改变,就需要重新建立优先队列。
#include<iostream> #include<cstdio> #include<queue> using namespace std; struct before_house{ int num,distance,place; }; struct after_house{ int num,distance,place; }; bool vis[100005]; int a[100005],dis[100005],n,mx,mxdis,far=-1,tempa,tempb,sum; priority_queue<before_house> before; priority_queue<after_house> after; bool operator < (before_house h1,before_house h2) { return h1.num<h2.num; } bool operator < (after_house h1,after_house h2) { return h1.num+(h1.distance-far)*2<h2.num+(h2.distance-far)*2; } //两种优先队列的比较方式不同,需要进行重载 void make_heap() { while(!before.empty()) { before.pop(); } while(!after.empty()) { after.pop(); } //清空 before_house bh; after_house ah; for(int i=1;i<=far;i++) { if(!vis[i]) { bh.num=a[i]; bh.distance=dis[i]; bh.place=i; before.push(bh); } } for(int i=far;i<=n;i++) { if(!vis[i]) { ah.num=a[i]; ah.distance=dis[i]; ah.place=i; after.push(ah); } } } int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>dis[i]; } for(int i=1;i<=n;i++) { cin>>a[i]; } for(int i=1;i<=n;i++) { if(a[i]+dis[i]*2>mx) { mx=a[i]+dis[i]*2; mxdis=i; } } cout<<mx<<endl; vis[mxdis]=1; far=mxdis; sum+=mx; make_heap(); for(int i=2;i<=n;i++) { if(!before.empty()) { tempb=before.top().place; } if(!after.empty()) { tempa=after.top().place; } if(a[tempb]>a[tempa]+(dis[tempa]-far)*2) { mxdis=tempb; mx=a[tempb]; before.pop(); vis[mxdis]=1; } else { mxdis=tempa; mx=a[tempa]+(dis[tempa]-far)*2; far=tempa; vis[mxdis]=1; make_heap(); } sum+=mx; printf("%d ",sum); } return 0; }
最后,祝大家NOIP2018 RP++