【POJ Challenge】生日礼物
Time Limit: 10 Sec Memory Limit: 128 MB
Description
ftiasch 18岁生日的时候,lqp18_31给她看了一个神奇的序列 A1, A2, ..., AN. 她被允许选择不超过 M 个连续的部分作为自己的生日礼物。
自然地,ftiasch想要知道选择元素之和的最大值。你能帮助她吗?
Input
第1行,两个整数 N (1 ≤ N ≤ 105) 和 M (0 ≤ M ≤ 105), 序列的长度和可以选择的部分。
第2行, N 个整数 A1, A2, ..., AN (0 ≤ |Ai| ≤ 104), 序列。
Output
一个整数,最大的和。
Sample Input
5 2
2 -3 2 -1 2
Sample Output
5
<br >
<br >
<br >
<br >
<br >
<br >
抱着线段树的心态来做这道题。。。结果怎么想怎么贪心。。。
先说一下步骤:
先同号的合并,然后把前后的负数直接去掉(最简单的贪心策略)
然后就是一通操作了。。。。先把所有正项加起来,然后特判一下之后,把每个数的绝对值存进堆里。
小根堆贪心,如果这个数原本是个正数,那么减去他代表不选他,如果是个负数,那么减去他代表合并他的左右两个。
为什么是对的呢?我们可以大致脑补一下:
首先,如果全是正数,显然去掉最小的。
先说为什么无脑选最小的正项删去不对:
假设已经初步处理过的数列是:6 -4 3 -2 7 选两段
如果我们直接去掉3的话就是13,但是如果我们合并后三项的话可以取到14.
那么这种bug什么时候会出现呢?我真的觉得我的破脑袋里不知道装了些啥,反正我是这样瞎想的:
假设我有一个数列A;
如果我把这个数列中的一个负项变成0,这个新数列B的答案不可能变的更差,对吧。
紧接着,我们把B中的一个正项变的更大,这样也不会让答案变的更差。
其实我们把一个负数进行合并就是这么一个过程。去掉一个负项和两个正项,然后加一个更好的正项,(在m允许的范围内是正确的)
这样,我们稍微思考一下就会发现,在m允许的情况下,上述方法是正确的。
(大致感受一下就好了。。。)
<br >
<br >
#include<bits/stdc++.h>
#define lpl pair<int, int>
using namespace std;
const int maxn = 1e5 + 5, INF = 211199921;
bool flag[maxn];
int n, m, s, t, ans, ini[maxn], num[maxn], l[maxn], r[maxn];
priority_queue< lpl, vector<lpl>, greater<lpl> > q;
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){ x = 10 * x + ch - '0'; ch = getchar();}
return x * f;
}
inline void putit()
{
t = read(); m = read(); s = 1;
for(int i = 1; i <= t; ++i){
ini[i] = read();
}
while(ini[t] <= 0) t--;
while(ini[s] <= 0) s++;
for(; s <= t; s++)
if((ini[s] > 0 && ini[s - 1] > 0) || (ini[s] <= 0 && ini[s - 1] <= 0))
num[n] += ini[s]; else num[++n] = ini[s];
//for(int i = 1; i <= n; ++i) printf("%d ", num[i]);
}
inline void workk()
{
for(int i = 1; i <= n; ++i)
if(num[i] > 0) {ans += num[i], m--;}
else num[i] = -num[i];
if(m >= 0){cout << ans; return;}
for(int i = 1; i <= n; ++i){
l[i] = i - 1, r[i] = i + 1, q.push(lpl(num[i], i));
}
m = -m; r[n] = 0; int x;
while(m--){
while(q.top().first != num[q.top().second]) q.pop();
x = q.top().second; q.pop(); ans -= num[x];
if(!l[x]){
num[r[x]] = INF; l[r[x]] = 0; continue;
}
if(!r[x]){
num[l[x]] = INF; r[l[x]] = 0; continue;
}
num[x] = num[l[x]] + num[r[x]] - num[x];
num[l[x]] = num[r[x]] = INF;
l[x] = l[l[x]]; r[x] = r[r[x]]; r[l[x]] = l[r[x]] = x;
q.push(lpl(num[x], x));
}
cout << ans;
}
int main()
{
putit();
workk();
return 0;
}