洛谷 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; }