码迷,mamicode.com
首页 > 其他好文 > 详细

单调队列

时间:2019-07-23 11:37:33      阅读:89      评论:0      收藏:0      [点我收藏+]

标签:++   type   href   距离   pac   连续子序列   ace   bit   前缀和   

最大子序和

输入一个长度为n的整数序列,从中找出一段不超过m的连续子序列,使得整个序列的和最大。

容易想到计算区间和,可以转换成两个前缀和相减,用S[i]表示前i项和,则连续子序列[L,R]中的数的和为S[R]-S[L-1].

所以原问题转化为找出两个位置x,y,使得s[y]-s[x]最大,且y-x<=M

暴力枚举O(n*m).

首先枚举右端点r,找左端点l,l范围为[r-m,r-1]

注意到:若k<j<i,且s[k]>=s[j],那么k永远不可能是最佳选择。

以上事实说明,可能成为最佳策略的集合一定是一个下标位置递增,对应的前缀和S的值也递增的序列。

用队列保存这一队列,随着右端点变,从前向后扫描,对每一个i:

1.判断队头决策与i的距离是否超过M的范围

2.此时队头就是右端点为i时,左端点j的最优选择。//因为队列中S[j]递增,所以队头元素一定最优

3.不断删除队尾决策,直到队尾对应的S的值小于S[i],然后把i作为一个新的决策入队 //维护单调性,删除的都不可能是最优决策

int l=1,r=1;
q[1]=0;//初始决策 j=0;
for(int i=1;i<=n;i++){
     while(l<=r&&q[l]<i-m)l++;//step1
     ans=max(ans,sum[i]-sum[q[l]]);//step2
     while(l<=r&&sum[q[r]]>=sum[i])r--;//step3
     q[++r]=i;
}

AC代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 ll A[300004];
 5 int n,m;
 6 ll q[300005];
 7 ll sum[300004];
 8 int main()
 9 {
10     scanf("%d%d",&n,&m);
11     for(int i=1;i<=n;i++){
12         scanf("%lld",&A[i]);
13         sum[i]=sum[i-1]+A[i];
14     }
15     ll ans=0;
16     int l=1,r=1;
17     q[1]=0;
18     for(int i=1;i<=n;i++){
19         while(l<=r&&q[l]<i-m)l++;
20         ans=max(ans,sum[i]-sum[q[l]]);
21         while(l<=r&&sum[q[r]]>sum[i])r--;
22         q[++r]=i;
23     }
24     cout<<ans<<\n;
25 }

 

单调队列

标签:++   type   href   距离   pac   连续子序列   ace   bit   前缀和   

原文地址:https://www.cnblogs.com/liulex/p/11230574.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!