博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
最大子序列和(单调队列算法)
阅读量:5745 次
发布时间:2019-06-18

本文共 1133 字,大约阅读时间需要 3 分钟。

题目大意:

给定一个长度为N的序列,请你求出它最大长度不超过M的最大子序列的和(其中 N,M<=3*10^5)

分析:

一般对于这样的题目,我们最现实想到的就是前缀和,通过枚举序列可以得到答案,但这样的时间复杂度显然是不乐观的(TLE)

所以我们可以通过队列来优化  (这个算法我们称之为单调队列算法

我们先枚举子序列的有端点 i 

此时问题转变为寻找一个 j 最为子序列的左端点 (i-m <= j <= i-1),使得是S[ j ] 最小 (S数组表示前缀和)

这时我们不妨再设一个 k ( k < j < i )并且S[ k ] <  S[ j ],那么对于所有i之后的子序列右端点 k 都不会成为最优的选择。因为 S[ j ] < S[ k ],并且 j > k ,j 更容易满足子序列长度小于 m 这一条件并且S[ i ] - S[ j ]得到的子序列和也更大,故当 j 出现后 k 就是一个无用的选择,在之后的计算的可以将其忽略

上述比较告诉我们,成为最优的选择的集合的一定是一个下标递增,其前缀和也递增的序列 

我们便可以用一个队列来实现这个过程(队列中保存的就是可供选择的子序列左端点)

1,判断队首的决策与 i 的距离是否不超过m

2,此时队首的决策就是子序列右端点为 i 的最优选择

3,不断删除队尾的决策直至队尾对应的前缀和小于 i 的前缀和,再把 i 做为一个新的决策入队

代码如下:

#include
#include
#include
using namespace std;#define N 300005int a[N];int sum[N];int q[N];int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } int l=1,r=1; int ans=0; q[1]=0; for(int i=1;i<=n;i++) { if(l<=r&&q[l]
=sum[i])r--; //第三步 q[++r]=i; } printf("%d\n",ans); return 0;}

 

转载于:https://www.cnblogs.com/xxjnoi/p/9210984.html

你可能感兴趣的文章
PHP-X开发扩展
查看>>
android学习笔记——onSaveInstanceState的使用
查看>>
工作中如何做好技术积累
查看>>
怎么用sysLinux做U盘双PE+DOS??
查看>>
Spring Transactional
查看>>
shell脚本实例
查看>>
我的友情链接
查看>>
Windows Phone 7 隔离存储空间资源管理器
查看>>
Microsoft Excel 2000/2003修复工具
查看>>
apache安装报错undefined reference ssl
查看>>
关于爱情只有一句忠告
查看>>
CentOS 7下安装部署Oracle11g图文教程
查看>>
F#初学笔记06
查看>>
实战:将企业域名解析委派给企业DNS服务器
查看>>
在Lync 2013环境部署Office Web Apps
查看>>
微软大会Ignite,你准备好了么?
查看>>
读书笔记-高标管事 低调管人
查看>>
Master带给世界的思考:是“失控”还是进化
查看>>
用户和开发者不满苹果iCloud问题多多
查看>>
java.lang.UnsatisfiedLinkError:no dll in java.library.path终极解决之道
查看>>