【算法•日更•第十七期】信息奥赛一本通1598:【 例 2】最大连续和题解

  废话不多说,直接上题:


 

1598:【 例 2】最大连续和


时间限制: 1000 ms         内存限制: 524288 KB
提交数: 303     通过数: 91 

【题目描述】

给你一个长度为 n 的整数序列 {A1,A2,,An},要求从中找出一段连续的长度不超过 m 的子序列,使得这个序列的和最大。

【输入】

第一行为两个整数 n,m;

第二行为 n 个用空格分开的整数序列,每个数的绝对值都小于 1000。

【输出】

仅一个整数,表示连续长度不超过 m 的最大子序列和。

【输入样例】

6 4
1 -3 5 1 -2 3

【输出样例】

7

【提示】

数据范围与提示:

对于 50% 的数据,1≤N,M≤104 ;

对于 100% 的数据,1N,M2×105 。

【来源】


  这道题明显和例一一样。都要用到单调队列优化动态规划,不过也有很多方法,我们来讲一讲。

  方法1:暴力

  用普通单调队列优化的方法,将k从1枚举到m。

  方法2:优先队列优化

  时间复杂度依旧很高。

  方法3:单调队列优化

  我们可以维护一个前缀和。那么这个前缀和是干什么的呢?我们可以依次来差分。

  我们需要维护一个i-k ~ i+1的区间(长度为m),找到一个最小的前缀和(这样就可以保证这个区间内的和最大),用pre[i+1]-这个前缀和就是这个区间的最大值了,那么用ans来记录一下最大值即可。

  同时要注意一个细节:看到别人的博客都有这样一句话if(n<=m) ans=max(ans,pre[n]);,这是为什么呢?当n=4,k=6时,数字分别是1,1,1,1,这种情况应该输出4,但是程序会输出3,这是为什么呢?因为队列一定不会是空的,也就是说一定会减去1,那么答案就错了。

  

  好了,详见注释,代码如下:

  

 1 #include<iostream>
 2 using namespace std;
 3 int n,m,a[1000000],num[1000000],q[1000000],pre[1000000],ans;
 4 inline void dp()
 5 {
 6     int head=1,tail=0;//标志队列为空 
 7     ans=pre[1];
 8     if(n<=m) ans=max(ans,pre[n]);//特判 
 9     for(int i=1;i<n;i++)//常规操作 
10     {
11         while(num[head]<i-m+1&&head<=tail) head++;
12         while(pre[num[tail]]>=pre[i]&&head<=tail) tail--;
13         num[++tail]=i;
14         ans=max(ans,pre[i+1]-pre[num[head]]);//记录ans 
15     }
16 }
17 int main()
18 {
19     cin>>n>>m;
20     for(int i=1;i<=n;i++)
21     {
22         cin>>a[i];
23         pre[i]=pre[i-1]+a[i];//记录前缀和 
24     }
25     dp();
26     cout<<ans;
27     return 0;
28 }
posted @ 2019-07-20 07:06  c1714-gzr  阅读(358)  评论(0编辑  收藏  举报