题干:
输入一个长度为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 6 41 -3 5 1 -2 3 7解题报告:
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_pairusing 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 41 -3 5 1 -2 31 -2 3 4 2 5*/在此处交题http://www.joyoi.cn/problem/tyvj-1305