【贪心 堆】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 }
View Code

第二次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 }
View Code

 


第一次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 }
View Code

正解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 }

 

posted @ 2018-03-08 20:29  AntiQuality  阅读(206)  评论(0编辑  收藏  举报