COGS 352 数字游戏

这是一道很基础很基础的单调队列的题,嗯很适合我来写。
这道题其实和单调队列裸题——滑动窗口(详见刘汝佳白书)是一样的,只要稍微改一下思考的方向。
一开始可以这么想,先预处理出前缀和,在前缀和数组pre[]上做文章。
对于每个i > s,即求出以每个i结尾的最优区间,再取最优即可。
所以就落到了求每个以i(i >= s)结尾的最优区间,我们有前缀和,所以就是求出前面某一段的最小值即可(其实不止单调队列这一种方法了),
“某一段”只要满足使这个以i结尾的区间长度在s到t之间即可。
那就是pre[i-t+s] ~ pre[i-s]之间的最小值了,用单调队列可以很快的处理。
这不就是滑动窗口了么,窗口长度是t-s,只不过求出最小值后再用pre[队尾+s]相减即可。
#include <cstdio>
#include <algorithm>
#define M 1000005
using namespace std;

int a, n, s, t, f, b, l, ans = -(1<<30);
int pre[M], q[M];

int main()
{
	freopen("ggame.in","r",stdin);
	freopen("ggame.out","w",stdout);
	scanf("%d %d %d", &n, &s, &t);
	l = t - s;
	for(int i = 1; i <= n; i++){
		scanf("%d", &a);
		pre[i] = pre[i-1] + a;	
	}
	ans = pre[s];
	for(int i = 1; i <= n-s; i++){
		while(f < b && pre[i] <= pre[q[b-1]]) b--;
		q[b++] = i;
		while(f < b && q[b-1] - q[f] > l) f++;
		ans = max(ans, pre[i+s] - pre[q[f]]);
	}
	printf("%d", ans);
}
不过我一开始不是这么想的,在我最初的想法因为细节wa掉(哎,这是病,得治!)的时候才这么做的。
我一开始想的是求出的数组pre[i]代表的是以i结尾,长度为s-1的区间和。
之后从s开始循环i,记录上一个(循环为i-1)最佳区间的左端点是pos,右端点既是i-1,
此时求的是以i为右端点的区间的左端点,
如果i-pos+1没有超过t,而且上一个最优值-pre[i-1] > 0(这一段就是我们时刻求的,是以每个i结尾的最优区间减去固定的pre[i]的那一小段,我们每一次求都是固定了一个长度s-1的区间,这个就是预处理的pre[]), 那么pos~i也就是最优的,
(如果上一个最优值pre[i-1] <= 0 那就可以只取第i-s+1个数即可)
否则,那么暴力枚举pos。
如此便可求出答案。
</pre></div><pre class="cpp" name="code">#include <cstdio>
#include <algorithm>
#define M 1000005
using namespace std;

int n, s, t, pos, cul, ans;
int a[M], pre[M];

int main()
{
	freopen("ggame.in","r",stdin);
	freopen("ggame.out","w",stdout);
	scanf("%d %d %d", &n, &s, &t);
	for(int i = 1; i <= n; i++) scanf("%d", a+i);
	
	for(int i = 2; i <= s; i++) pre[s] += a[i];
	for(int i = s+1; i <= n; i++) pre[i] = pre[i-1] + a[i] - a[i-s+1]; 
	
	cul = ans = pre[s] + a[1]; pos = 1;
	for(int i = s+1; i <= n; i++){
		if(pos == 1 || i-pos+1 > t){
			int mx = -(1<<30), sum = 0;
			for(int j = 0; j < t-s+1 && i-s+1-j > 0; j++){
				sum += a[i-s+1-j];
				if(sum > mx){
					mx = sum; pos = i-s+1-j;
				}
				cul = mx+pre[i];
			} 
		} else if(cul-pre[i-1] < 0){
			pos = i-s+1, cul = pre[i] + a[i-s+1];
		} else{
			cul += a[i];
			if(cul < a[i]) cul = a[i];
		}
		ans = max(ans, cul);
	}
	printf("%d", ans);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值