【贪心 堆】luoguP2672 推销员
堆维护,贪心做法
题目描述
阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有N家住户,第i家住户到入口的距离为Si米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的X家住户推销产品,然后再原路走出去。
阿明每走1米就会积累1点疲劳值,向第i家住户推销产品会积累Ai点疲劳值。阿明是工作狂,他想知道,对于不同的X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。
输入输出格式
输入格式:
第一行有一个正整数N,表示螺丝街住户的数量。
接下来的一行有N个正整数,其中第i个整数Si表示第i家住户到入口的距离。数据保证S1≤S2≤…≤Sn<10^8。
接下来的一行有N个正整数,其中第i个整数Ai表示向第i户住户推销产品会积累的疲劳值。数据保证Ai<10^3。
输出格式:
输出N行,每行一个正整数,第i行整数表示当X=i时,阿明最多积累的疲劳值。
这题在2016年做过,不过全WA了hhh……
好吧今天提交4次前两次也是全WA……
最初的思路
假设现在右端点为i-1,有i优于j(i < j)那么有wi+2(s[i]-s[i-1])>wj+2(s[j]-s[i-1])即wi+2si>wj+2sj
定义距离s[],疲劳值w[],f[i]表示在i..n中(2si+wi)最大值的编号。那么我们每次处理1..now-1的w[]最大值和value(f[i+1])两者最大值。重复这一过程。
然而这个做法在调试时候就叉掉了……
第一次WA
非常NAIVE地把左右两边用了一个堆维护value()最小值,pretest过了就交了……
但是这个value()与max_right_border有关系,所以这个堆是个假堆……
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5; 4 int n,s[N],w[N],f[N],now,sum,rx; 5 bool vis[N]; 6 int reval(int a) 7 { 8 if (a >= rx)return w[a]+2*(s[a]-s[rx]); 9 return w[a]; 10 } 11 struct cmp 12 { 13 bool operator () (int &a, int &b) const 14 { 15 return reval(a) < reval(b); 16 } 17 }; 18 priority_queue<int, vector<int>, cmp> p,q; 19 int deal(int x) 20 { 21 if (x <= 0)return 0; 22 while ((!q.empty())&&vis[q.top()])q.pop(); 23 if (q.empty())return 0; 24 return q.top(); 25 } 26 int dear(int x) 27 { 28 while ((!p.empty())&&(vis[p.top()]))p.pop(); 29 if (p.empty())return 0; 30 return p.top(); 31 } 32 int main() 33 { 34 scanf("%d",&n); 35 for (int i=1; i<=n; i++)scanf("%d",&s[i]); 36 for (int i=1; i<=n; i++){scanf("%d",&w[i]);p.push(i);} 37 f[n] = n; 38 for (int i=n-1; i>=1; i--) 39 if (2*s[i]+w[i] > 2*s[f[i+1]]+w[f[i+1]]) 40 f[i] = i; 41 else f[i] = f[i+1]; 42 now = 0;rx = 0; 43 for (int i=1; i<=n; i++) 44 { 45 int dl = deal(now-1); 46 int dr = dear(now+1); 47 if (reval(dl) > reval(dr)){ 48 now = dl; 49 vis[dl] = 1; 50 sum += reval(dl); 51 }else{ 52 sum += reval(dr); 53 if (rx < dr){ 54 for (int j=now+1; j<=dr; j++) q.push(j); 55 rx = dr; 56 } 57 now = dr; 58 vis[dr] = 1; 59 } 60 printf("%d\n",sum); 61 } 62 return 0; 63 }
第二次WA
观察变量调试重写了三十分钟,(此时并没有意识到假堆的严重性),看着调试输出还以为堆不假了(flag)。
在查询时候,又多放了一个堆来解决:查询左边时候q.top()却在now右边的情况(反之亦然)。这pretest设计得真好
按道理这样应该比第一次WA要慢一些,结果却快了200ms+...
反正都是全WA
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5; 4 int n,s[N],w[N],f[N],now,sum,rx; 5 bool vis[N]; 6 int reval(int a) 7 { 8 if (a >= rx)return w[a]+2*(s[a]-s[rx]); 9 return w[a]; 10 } 11 struct cmp 12 { 13 bool operator () (int &a, int &b) const 14 { 15 return reval(a) < reval(b); 16 } 17 }; 18 priority_queue<int, vector<int>, cmp> p,q; 19 int deal(int x) 20 { 21 if (x <= 0)return 0; 22 queue<int>tt; 23 while ((!q.empty())&&(q.top()>now||vis[q.top()])) 24 { 25 while ((!q.empty())&&(vis[q.top()]))q.pop(); 26 while ((!q.empty())&&(q.top()>now)){tt.push(q.top());q.pop();} 27 } 28 if (q.empty())return 0; 29 int xx = q.top(); 30 while (!tt.empty()){q.push(tt.front());tt.pop();} 31 return xx; 32 } 33 int dear(int x) 34 { 35 queue<int>tt; 36 if (rx == n)return 0; 37 if(rx!=n)while ((!p.empty())&&(p.top()<now||vis[p.top()])) 38 { 39 while ((!p.empty())&&(vis[p.top()]))p.pop(); 40 // printf("ptop:%d\n",p.top()); 41 while((!p.empty())&&(p.top()<now)){tt.push(p.top());p.pop();} 42 } 43 if (p.empty())return 0; 44 int xx = p.top(); 45 while (!tt.empty()){p.push(tt.front());tt.pop();} 46 return xx; 47 } 48 int main() 49 { 50 scanf("%d",&n); 51 for (int i=1; i<=n; i++)scanf("%d",&s[i]); 52 for (int i=1; i<=n; i++){scanf("%d",&w[i]);p.push(i);} 53 f[n] = n; 54 for (int i=n-1; i>=1; i--) 55 if (2*s[i]+w[i] > 2*s[f[i+1]]+w[f[i+1]]) 56 f[i] = i; 57 else f[i] = f[i+1]; 58 now = 0;rx = 0; 59 // for (int i=1; i<=n; i++)printf("%d ",f[i]);printf("#\n"); 60 for (int i=1; i<=n; i++) 61 { 62 int dl = deal(now-1); 63 int dr = dear(now+1); 64 // printf("now:%d rx:%d dl:%d %d-dr:%d %d\n",now,rx,dl,reval(dl),dr,reval(dr)); 65 if (reval(dl) >= reval(dr)){ 66 now = dl; 67 vis[dl] = 1; 68 sum += reval(dl); 69 }else{ 70 sum += reval(dr); 71 if (rx < dr){ 72 for (int j=rx+1; j<=dr; j++) q.push(j); 73 rx = dr; 74 } 75 now = dr; 76 vis[dr] = 1; 77 } 78 // for (int i=1; i<=n; i++)printf("%d ",vis[i]);puts("@"); 79 printf("%d\n",sum); 80 } 81 int dl = deal(now-1); 82 int dr = dear(now+1); 83 // printf("now:%d rx:%d dl:%d %d-dr:%d %d\n",now,rx,dl,reval(dl),dr,reval(dr)); 84 return 0; 85 }
第一次TLE
好气啊打了一发纯粹的暴力(天哪有60分)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5+35; 4 int n,s[N],w[N],rx,now,sum; 5 int a,b; 6 bool vis[N]; 7 int reval(int x) 8 { 9 if (x < rx)return w[x]; 10 return w[x] + 2*(s[x]-s[rx]); 11 } 12 int main() 13 { 14 scanf("%d",&n); 15 for (int i=1; i<=n; i++)scanf("%d",&s[i]); 16 for (int i=1; i<=n; i++)scanf("%d",&w[i]); 17 for (int k=1; k<=n; k++) 18 { 19 a = 0, b = 0; 20 int cmp = 0, lb; 21 for (int i=1; i<now; i++) 22 if ((!vis[i])&&(a < w[i]))a = w[b = i]; 23 cmp = a;lb = b; 24 a = 0,b = 0; 25 for (int i=now+1; i<=n; i++) 26 if ((!vis[i])&&(a < reval(i))) 27 a = reval(i),b = i; 28 if (a > cmp){cmp = a;lb = b;} 29 rx = max(rx, lb); 30 vis[lb] = 1; 31 now = lb; 32 sum += cmp; 33 printf("%d\n",sum); 34 } 35 return 0; 36 }
正解AC
在打完暴力之后豁然开朗(???)意识到右部分大于max_right_border的枚举即可,这样一来左边的就变为小于max_right_border的部分,由此无所谓在now左部分的元素变动情况,从而可以保证堆的正确性。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5+35; 4 int n,s[N],w[N],rx,now,sum; 5 int a,b; 6 bool vis[N]; 7 struct cmp 8 { 9 bool operator() (int &a, int &b) 10 { 11 return w[a] < w[b]; 12 } 13 }; 14 priority_queue<int, vector<int>, cmp>q; 15 void find() 16 { 17 while ((!q.empty())&&(vis[q.top()]))q.pop(); 18 if (q.empty())return; 19 b = q.top();a = w[b]; 20 } 21 int main() 22 { 23 scanf("%d",&n); 24 for (int i=1; i<=n; i++)scanf("%d",&s[i]); 25 for (int i=1; i<=n; i++)scanf("%d",&w[i]); 26 for (int k=1; k<=n; k++) 27 { 28 a = 0, b = 0; 29 int cmp = 0, lb; 30 find(); 31 cmp = a;lb = b; 32 a = 0,b = 0; 33 for (int i=rx+1; i<=n; i++) 34 if ((!vis[i])&&(a < w[i] + 2*(s[i]-s[rx]))) 35 a = w[i] + 2*(s[i]-s[rx]),b = i; 36 if (a > cmp) 37 { 38 cmp = a; 39 lb = b; 40 } 41 if (rx < lb) 42 { 43 for (int i=rx+1; i<=lb; i++)q.push(i); 44 rx = lb; 45 } 46 vis[lb] = 1; 47 now = lb; 48 sum += cmp; 49 printf("%d\n",sum); 50 } 51 return 0; 52 }