Max Sum of Max-K-sub-sequence
HDU - 3415题意:找出循环数列中长度不超过k的最大的子段和;
要找的子段的长度不超过k,普通思路就是由长度为1找到长度为k;O(nk)的复杂度,k小点还好说, 如果k很大就不可行了;
先想到了前缀和,sum[i]-min(sum[i-k~i-1])就是以i结尾的长度不超过k的最大的子段和;
那么是不是可以维护一个队列使得队首时sum[i-k~i-1]最小的?可以单调队列可以办到; 然后队列中的元素一定不超过k个;
有什么用呢?
既然队列的队首时min(sum[i-k~i-1])所以sum[i]-队首一定是最大的;而且满足区间长度不超过k;
接下来每次比较维护一个首尾便于输出就OK了;
还有一点队列里放的是下标,方便记录;队列用双向队列,STL里的deque就行,因为如果队列中元素超了k个就要删除队首,双向队列方便操作队首;
敲黑板!!!队列中的元素并不一定是连续的,所以不能靠计算队列中的元素个数来判断区间长度是否超过k,要通过队首的下标判断;
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
int n, k;
int ans, sum[maxn*2], a[maxn*2];
deque<int> deq;
int main(){
int t;
scanf("%d", &t);
while(t--){
scanf("%d%d", &n, &k);
for(int i=1; i<=n; i++){
scanf("%d", &a[i]);
a[i+n]=a[i];
}
sum[0]=0;
for(int i=1; i<n+k; i++){
sum[i]=sum[i-1]+a[i];
}
int ans=-INF, head=0, tail=0;
deq.clear();
for(int i=1; i<n+k; i++){
while(!deq.empty()&&sum[i-1]<sum[deq.back()]) deq.pop_back();
deq.push_back(i-1);
while(!deq.empty()&&deq.front()<i-k) deq.pop_front();
if(sum[i]-sum[deq.front()]>ans){
ans=sum[i]-sum[deq.front()];
head=(deq.front()+1);
tail=i%n;
}
}
printf("%d %d %d\n", ans, head, tail==0?n:tail);
}
return 0;
}