洛谷 P2672 推销员
嗯...
题目链接:https://www.luogu.org/problem/P2672
这个题是一个贪心的思想...
我们会发现本题有一个特性,就是如果我们走到一个更远的地方,那么近的地方距离原点的距离我们可以忽略.
最后疲劳值是由两部分组成的:路径疲劳值和推销疲劳值.又因为第一行提到的,所以我们可以选一个点k(后面解释),将每个状态下分为两种点:
1.在当前k点的左面,这些点的疲劳值其实只有推销疲劳值,因为我们已经走得更远了,所以顺道处理这些就行,不需要走多余的路
2.比当前k点的右面,这些点的疲劳值其实就是推销疲劳值+两倍路径疲劳值-两倍k的路径疲劳值
而其实最大疲劳值就是这两种点各自的最大值的最大值,所以我们可以用两个大根堆来存,取两个堆顶的较大值即可.
返回来,k是什么呢?k就是我们当前已经选过的点里最靠右的
为什么呢?因为对我们来说只要某个点走了,则这个点左边所有点我们都可以顺路经过,并不需要多余路径疲劳值,所以我们只要记录最右即可.
代码处理的一些细节:
1.一开始右边的堆将所有点放进去,左边堆为空,选一个最大点为第一个k
2.每当更新一个k,再将k左边所有的点推进左边堆
3.那么我们更新k后怎么判断右边堆那些在当前k的左边呢?因为k是动态的,所以原来在右边的可能到右边我们只要判断当前元素和k谁到原点近即可,
所以我们要不单要记录疲劳值,还要记录路径值,所以我们要定义结构体来构造堆
//思路来自PD大佬lpy:https://www.cnblogs.com/lipeiyi520/p/11037364.html
外加自己的优化(优化掉了很多东西
AC代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<queue> 5 6 using namespace std; 7 8 const int maxn = 100005; 9 10 struct node{ 11 int w, id, ans; 12 node() { } 13 bool operator < (const node & p) const{ 14 return w + 2 * id < p.w + 2 * p.id; 15 }//重载运算符 16 } e[maxn]; 17 18 19 priority_queue <int> a;//左边点 20 priority_queue <node> b;//右边点 21 22 int m, cnt, n, k, j; 23 24 inline void fir(){ 25 m = b.top().ans;//第一个值为最大值 26 printf("%d\n", m); 27 cnt = n - 1;//cnt用来记录当前i 28 k = b.top().id;//k转移到最大值的id 29 b.pop(); 30 for(j = 1; j <= n; j++){ 31 if(e[j].id >= k) break;//右边点 32 else a.push(e[j].w);//左边点 33 }//更新 34 }//第一次 35 36 inline void solve(){ 37 while(cnt--){ 38 if(b.top().ans - 2 * k >= a.top()){ 39 if(b.top().id <= k){//更新,若原右边点现在在左边 40 b.pop(); 41 cnt++; 42 continue; 43 }//右边堆顶比左边堆顶大 44 m += b.top().ans - k * 2;//加最大值 45 k = b.top().id;//k转移 46 b.pop(); 47 while(j++ <= n){ 48 if(e[j].id >= k) break; 49 else{ 50 a.push(e[j].w); 51 b.pop(); 52 }//右边转移到左边 53 } 54 } 55 else{ 56 m += a.top(); 57 a.pop(); 58 }//左边堆顶大 59 printf("%d\n", m); 60 } 61 } 62 63 64 int main(){ 65 scanf("%d", &n); 66 for(int i = 1; i <= n; i++) scanf("%d", &e[i].id); 67 for(int i = 1; i <= n; i++){ 68 scanf("%d", &e[i].w); 69 e[i].ans = e[i].w + 2 * e[i].id; 70 b.push(e[i]); 71 } 72 fir(); 73 solve(); 74 return 0; 75 }