洛谷 P1714 切蛋糕
Description
今天是小Z的生日,同学们为他带来了一块蛋糕。这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值。
小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大,但小Z最多又只能吃M小块(M≤N)的蛋糕。
吃东西自然就不想思考了,于是小Z把这个任务扔给了学OI的你,请你帮他从这N小块中找出连续的k块蛋糕(k≤M),使得其上的幸运值最大。
Input
输入文件cake.in的第一行是两个整数N,M。分别代表共有N小块蛋糕,小Z最多只能吃M小块。
第二行用空格隔开的N个整数,第i个整数Pi代表第i小块蛋糕的幸运值。
Output
- 输出文件cake.out只有一行,一个整数,为小Z能够得到的最大幸运值。
Sample Input
6 3 1 -2 3 -4 5 -6
Sample Output
5
Data Size
对20%的数据,N≤100。
对100%的数据,N≤500000,|Pi|≤500。 答案保证在2^31-1之内。
题解:
单调队列。
因为序列是连续的,而且不停的在滑动。所以我就想到了单调队列。然后我就想着普通dp怎么写?首先求一个前缀和,那么枚举右端点,右端点确定了,就在位置(右端点 - m) ~ 位置(右端点)之间找一个最小的前缀和。两数一减,每个右端点取一次max。那么复杂度是O(nm)。既然想到了用单调队列,那么优化掉找最小前缀和这一步就好了。
#include <iostream>
#include <cstdio>
#include <deque>
#define N 500005
using namespace std;
struct Node {int val, pos;};
deque<Node> que;
int n, m, ans = -0x7fffffff;
int sum[N];
int read()
{
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
return x *= f;
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
sum[i] = read();
sum[i] += sum[i - 1];
}
que.push_back((Node){0, 0});
for(int i = 1; i <= n; i++)
{
ans = max(ans, sum[i] - que.front().val);
while(que.size() && que.front().pos + m <= i) que.pop_front();
while(que.size() && que.back().val >= sum[i]) que.pop_back();
que.push_back((Node){sum[i], i});
}
cout << ans;
return 0;
}