单调队列——小游戏

·今天考试题!!然鹅爆了!!

·题目内容

1 .Background
CZR虽然已经是个大学生了,但是他还是喜欢在家里偷偷玩跳格子.
2. Description
CZR的家里有连续的N + 1块地砖,编号为0N,他在每一块地砖上
都写上了一个数字.
一开始他在0号地砖,这块地砖的分数为0,每次他都会后面跳一步,
但是他不一定需要跳到最后.
因为他跳远能力不行,所以他一步只能往后跳LR个格子,也就是说
在地砖i时,他只能跳到地砖i + Li + 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;
}

猜你喜欢

转载自www.cnblogs.com/konglingyi/p/11391081.html