题干:
输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。
例如 1,-3,5,1,-2,3
当m=4时,S=5+1-2+3=7
当m=2或m=3时,S=5+1=6
输入格式
第一行两个数n,m
第二行有n个数,要求在n个数找到最大子序和
输出格式
一个数,数出他们的最大子序和
提示
数据范围:
100%满足n,m<=300000
样例数据
输入样例 #1 | 输出样例 #1 |
---|---|
| |
解题报告:
dp[i]表示到第i个数的不超过m的最大连续子段和,sum[i]表示i的前缀和
dp[i]=max(sum[i]-sum[k])(i=> k >=i-m),所以要找在窗口内的最小的sum[k],因此用单调队列优化一下。
然后这里dp不用开数组了,你直接拿个ans记录一下最大值就行,因为更新过一次之后就没有用过。(当然你也可以最后扫一遍dp数组输出最大值)
注意你要用的是sum[i]-sum[ (i-m+1) -1],所以你第一个while要写成<i-m,而不是i-m+1。
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define F first
#define S second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 3e5 + 5;
ll a[MAX],sum[MAX],ans;
int n,m;
deque<ll> dq;
int main()
{ans = -99999999;cin>>n>>m;for(int i = 1; i<=n; i++) scanf("%lld",a+i),sum[i] = sum[i-1] + a[i];dq.push_back(0); for(int i = 1; i<=n; i++) {while(!dq.empty() && dq.front() < i-m) //注意这里不能用dq.size()判断!因为只能说明双端队列中有m个元素,但是不能证明就是连续的m个,有可能队首元素是a[1]呢?(因为有可能中间的元素都在下一个while中被pop了) dq.pop_front();while(!dq.empty() && sum[dq.back()] >= sum[i]) dq.pop_back();dq.push_back(i); ans = max(sum[i] - sum[dq.front()],ans);}printf("%lld\n",ans);return 0 ;
}
/*
6 4
1 -3 5 1 -2 3
1 -2 3 4 2 5
*/
在此处交题http://www.joyoi.cn/problem/tyvj-1305