最大子序和(单调队列优化)

最大子序列和

输入一个长度为 n 的整数序列,序列中可能有负数,从中找出一段长度不超过 m的连续子序列,使得子序列中所有数的和最大。
说明: 子序列的长度至少是 1。
输入格式
第一行输入两个整数 n,m。
第二行输入 n个数,代表长度为 n的整数序列。
同一行数之间用空格隔开。
输出格式
输出一个整数,代表该序列的最大子序和。
数据范围
1≤n,m≤300000
输入样例:
8 5
2 -1000 -500 2 8 -500 -700 0
输出样例:
10

解析

此题数据范围偏大,暴力枚举不合适。
用到的知识点:区间和;前缀和相减; 单调队列

原问题转化为: 找出两个位置x,y, 使S[y] - S[x] 最大,且 y - x <= m ,S是前缀和数组。
可以枚举右侧端点 i, 当 i 固定时,问题变为: 找到一个左端点 j,S[j]最小,j 属于[i - m, i - 1]
比较任意两个位置 j 和 k , 如果 k < j < i 且 S[k] >= S[j] ,那么对于所有大于等于 i 的右端点,k 永远不会成为最优选择。
因为不仅 S[k] 大于等于 S[j] ,而且 j 离 i 更近,长度更不容易超过 m,显然 j 的生存能力比 k 更强。所以当 j 出现(进队)后, k 就完全是一个无用的位置下标。
以上分析说明,可能成为最优选择的策略集合一定是: 下标位置严格递增,对应的前缀和S 的值也严格递增 的序列。可以用队列保存这个序列,m >= 序列长度 >= 1 。
单调队列算法时间复杂度大致为O(N) : 每个位置的元素最多入队一次,出队一次。

#include <iostream>
#include <limits.h>
using namespace std;

const int N = 300010;
typedef long long ll;
ll res = INT_MIN;
int n, m;// m is window 
int q[N];
ll s[N];


int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
	{
		cin >> s[i];
		s[i] += s[i -1];
	}
	int hh = 0, tt = 0;
	for(int i = 1; i <= n; i ++)
	{
		if(i - q[hh] > m) hh ++;//out of date
		res = max(res,s[i] - s[q[hh]]);
		while(hh <= tt && s[q[tt]] >= s[i]) tt --;
		q[++tt] = i;
	}
	
	cout << res << endl;
  return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值