单调队列——小游戏
·今天考试题!!然鹅爆了!!
·题目内容:
1 .Background
CZR虽然已经是个大学生了,但是他还是喜欢在家里偷偷玩跳格子.
2. Description
CZR的家里有连续的N + 1块地砖,编号为0到N,他在每一块地砖上
都写上了一个数字.
一开始他在0号地砖,这块地砖的分数为0,每次他都会后面跳一步,
但是他不一定需要跳到最后.
因为他跳远能力不行,所以他一步只能往后跳L到R个格子,也就是说
在地砖i时,他只能跳到地砖i + L到i + R中的一块地砖.
他进行一次游戏的分数总和是他跳到的所有地砖的分数之和.
他现在想获得最大的分数,来证明自己的数学能力很强,所以他需要
知道最大分数是多少.
·题目来源:山东济南集训考试题二第二题
·题目思路:
令F[i]表示CZR跳到格子i上的时候,他能获得的最大分数.
则F[i] = max(F[i – L]…F[i - R]) + a[i].(a[i]表示i点的分数)
我们想要优化这个max(F[i – L]…F[i - R]).
这个i是取遍1-n的.
简单的来说就是要求一个区间最大值,区间长度是固定的.
有很多方法可以来做:
一个最简单的方法就是单调队列.
求区间最大值,区间固定,这显然是简单的单调队列的优化题.
而且单调队列也很好写,分为进队和出队两个步骤就好了.
使用单调队列的时间复杂度是O(n)的.
问题再扩大化一点,可以变成:
询问一个数列中的区间最大值,区间长度不固定.
因为不带修改,只有询问,所以这明显就是一个st表.
转移的时候对st表进行一次查询就好了.
时间复杂度为O(nlogn + n).
当然不用st表依旧可以做.
我们再把问题扩大化一点:
一个集合,有三个操作:
加入一个数;删除一个数;求当前集合内数的最大值.
对于只有 加入一个数+ 求当前集合内数的最大值一个堆就完成了.
删除一个数可以再建立一个堆存储被删掉的数.
每次查询的时候查看两个堆的堆顶元素就好了.
时间复杂度O(nlogn).
·板子:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 500005; int n, l, r; int a[N], f[N]; int head, tail; int ans; int main() { //freopen("jump.in", "r", stdin); //freopen("jump.out", "w", stdout); scanf("%d\n", &n); scanf("%d%d", &l, &r); for (int i = 1; i <= n; ++i) scanf("%d", a[i]); memset(f, 0, sizeof(f)); for (int i = l; i <= n; ++i) { f[i] = 0; for (int j = min(0, i - r); j <= i - l; ++j) f[i] = max(f[i], f[j]); f[i] += a[i]; ans = max(ans, f[i]); } printf("%d\n", ans); //fclose(stdin); //fclose(stdout); return 0; }