【BZOJ2288】生日礼物 [贪心]

生日礼物

Time Limit: 10 Sec  Memory Limit: 128 MB
[Submit][Status][Discuss]

Description

  ftiasch 18岁生日的时候,lqp18_31给她看了一个神奇的序列 A1, A2, ..., AN. 她被允许选择不超过 M 个连续的部分作为自己的生日礼物。

  自然地,ftiasch想要知道选择元素之和的最大值。你能帮助她吗?

Input

  第1行,两个整数 N  和 M , 序列的长度和可以选择的部分。

  第2行, N 个整数 A1, A2, ..., AN , 序列。

Output

  一个整数,最大的和。

Sample Input

  5 2
  2 -3 2 -1 2

Sample Output

  5

HINT

  1 ≤ N ≤ 105, 0 ≤ M ≤ 105, 0 ≤ |Ai| ≤ 104

Solution

  首先,我们可以把权值正负相同的连续的一段合并起来。Ans+=(所有正数),块数++。

  然后把每一段的绝对值加入到小根堆里面。每次贪心取出最小的来,块数减去 1 直到满足题目要求为止。

  为什么这样可以对呢?我们来讨论一下:

    1. 如果删去的段是正数, 那么相当于不取这个

    2. 如果删去的段是负数,那么相当于取了这个段合并它左右的两个段。

  但是!这样会有一个问题!就是无法考虑连续取5个段及以上的情况,并且无法保证:取了一个数不取相连的两个数(会导致块数不减)。

  所以判断一下,每次取段的时候,删去左右两个小段加上一个大段他们三个合并的值)即可。

Code

  1 #include<iostream>    
  2 #include<string>    
  3 #include<algorithm>    
  4 #include<cstdio>    
  5 #include<cstring>    
  6 #include<cstdlib>
  7 #include<cmath>
  8 #include<queue>
  9 using namespace std;  
 10 typedef long long s64;
 11   
 12 const int ONE = 200005;
 13 const int INF = 1000000007;
 14 
 15 int n, m;
 16 int a[ONE], A[ONE];
 17 int pre[ONE], suc[ONE];
 18 int Ans, block;
 19 
 20 struct power
 21 {
 22         int id, val;
 23         bool operator <(power a) const
 24         {
 25             return a.val < val;
 26         }
 27 };
 28 priority_queue <power> q;
 29 
 30 int get()
 31 {    
 32         int res=1,Q=1;char c;    
 33         while( (c=getchar())<48 || c>57 ) 
 34         if(c=='-')Q=-1; 
 35         res=c-48;     
 36         while( (c=getchar())>=48 && c<=57 )    
 37         res=res*10+c-48;    
 38         return res*Q;
 39 }
 40 
 41 
 42 int main()
 43 {
 44         n = get();    m = get();
 45         for(int i = 1; i <= n; i++) a[i] = get();
 46         int from = 1;    while(a[from] <= 0) from++;
 47         int to = n;        while(a[to] <= 0) to--;
 48         
 49         n = 0;
 50         for(;from <= to;)
 51         {
 52             if( (a[from-1] <= 0 && a[from] <= 0) || (a[from-1] > 0 && a[from] > 0)) 
 53             A[n] += a[from];
 54             else A[++n] = a[from];
 55             from++;
 56         }
 57         
 58         for(int i = 1; i <= n; i++)
 59         {
 60             pre[i] = i - 1;
 61             suc[i] = i + 1;
 62             if(A[i] > 0) Ans += A[i], block++;
 63             A[i] = abs(A[i]);
 64             q.push( (power){i, A[i]} );
 65         }    
 66         
 67         if(block <= m) {printf("%d", Ans); return 0;}
 68         
 69         pre[1] = suc[n] = 0;
 70         
 71         for(;;)
 72         {
 73             for(;;)
 74             {
 75                 power u = q.top();
 76                 if(u.val != A[u.id]) q.pop();
 77                 else break;
 78             }
 79             
 80             power u = q.top();    q.pop();
 81             Ans -= u.val;
 82             
 83             if(pre[u.id] == 0)    A[suc[u.id]] = INF, pre[suc[u.id]] = 0;
 84             else
 85             if(suc[u.id] == 0)    A[pre[u.id]] = INF, suc[pre[u.id]] = 0;
 86             else
 87             {
 88                 A[u.id] = A[pre[u.id]] + A[suc[u.id]] - A[u.id];
 89                 A[pre[u.id]] = A[suc[u.id]] = INF;
 90                 pre[u.id] = pre[pre[u.id]];
 91                 suc[u.id] = suc[suc[u.id]];
 92                 pre[suc[u.id]] = suc[pre[u.id]] = u.id;
 93                 q.push( (power){u.id, A[u.id]} ); 
 94             }
 95             
 96             block--;    if(block <= m) break;
 97         }
 98         
 99         printf("%d", Ans);
100 }
View Code

 

 

posted @ 2017-07-28 16:57  BearChild  阅读(534)  评论(0编辑  收藏  举报