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; }