题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3415
给你一个可循环的数列,1——n
然后给你一个数字k
‘要你求出长度不大于k的最大子串和
我们用sum数字求出1~i的连续和
然后对于j,我们只需要找到一个i,i>=j-k+1,且sum[i-1]最小
那么对于j来说,这个就是我们可以求出来的最大子串和
然后用过o(n)求出答案
但是对于每个j,都会有很多不同的i,我们就要用单调队列来维护
即要找到符合条件的最小的sum[i-1],即i-1>=j-k,因为对于每个j来说
求子串和使用s[j]-s[j-1]来求i~j的子串和的
弄清楚了题意,解题就很方便了
直接用deque来实现单调队列
对于每一个j-1,如果它当前比队尾还小,则不停的删除,直到可以插入
然后还要检查头部
如果当前的头部,即q.front()如过小于j-k(j是我们当前的尾部),那么就删除头部
这样一来,就使头部就是我们要的那个i,然后就比较已知的ans,进行选择
下面上代码:
#include<cstdio> #include<cstring> #include<deque> using namespace std; const int maxn = 100000+20; int sum[maxn*2]; int a[maxn*2]; int t,n,k; deque<int> q; int main() { scanf("%d",&t); while(t--) { scanf("%d %d",&n,&k); sum[0]=0; int tmp; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); a[n+i]=a[i]; sum[i]=sum[i-1]+a[i]; } for(int i=n+1;i<=n+k;i++) { sum[i]=sum[i-1]+a[i]; } q.clear(); int ans = -0x3f3f3f3f; int s,e; for(int i=1;i<=n+k;i++) { while(!q.empty() && sum[i-1]<sum[q.back()]) q.pop_back(); q.push_back(i-1); while(q.front()<i-k) q.pop_front(); if(sum[i]-sum[q.front()] > ans) { ans=sum[i]-sum[q.front()]; s=q.front()+1; e=i; } } printf("%d %d %d\n",ans,s,e>n?e%n:e); } return 0; }