Max Sum of Max-K-sub-sequence
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5673 Accepted Submission(s): 2049
Now your job is to calculate the max sum of a Max-K-sub-sequence. Max-K-sub-sequence means a continuous non-empty sub-sequence which length not exceed K.
Then T lines follow, each line starts with two integers N , K(1<=N<=100000 , 1<=K<=N), then N integers followed(all the integers are between -1000 and 1000).
4 6 3 6 -1 2 -6 5 -5 6 4 6 -1 2 -6 5 -5 6 3 -1 2 -6 5 -5 6 6 6 -1 -1 -1 -1 -1 -1
7 1 3 7 1 3 7 6 2 -1 1 1
解题思路:
求长度不超过K的最大子段和。这道题看了很多解题报告,折腾了快两天,终于理解得差不多了。自己后来写了一遍代码,一次AC。
参考:http://www.cnblogs.com/neverforget/archive/2011/10/13/ll.html
维护一个单调递增的队列,队首元素一定是最小的,sum[j]-sum[i],j是一定的,为区间尾,就是找最小的sum[i],而这个sum[i]一定是存在队首的。之前看过一个结题报告说,本题为了维护单调,在插入元素前要删除队尾元素,在被删除的元素其实并不是真正的被删除,还是存在队列中,后来想想,也是这样回事,因为虽然删除了元素,但长度K是一定的,这个长度K是不能根据当前队列里有多少元素来算,而是要根据下标,而这些下标就包括队尾已经被删除的元素(这种情况被插入的元素不是队首),如果把尾节点全部删除了,当前插入的元素为队首,那么那些删除的元素可以当做已经不存在了,区间长度由当前队首元素(下标)来计算。队列里最多只能有K个元素,这里元素都是指的下标。一开始被插入的元素为下标0.
Sum[j]- sum[i] =sum[i+1]+sum[i+2]+…..+sum[j]
比如数据
Num: 2 -1 8 -5 K=3
Sum : 2 1 9 4 sum[0]=0
下标 1 2 3 4
I=1 插入 0
I=2 插入 1 单调递增 sum[0]<sum[1]
I=3 sum[2]<sum[1],删除1 ,插入2 ,这时候可以算出前三个数的和,这也是第一个长度为K 的序列和。
I=4 因为坐标限制0<4-K,删除首元素,插入3,这时候队列里有2 3,想想这时候为什么这时候要删除1那个元素,sum[4]-sum[1]= 2 sum[4]-sum[2]= 3 ,后面那个是最优的,所以这也是优先队列首元素为最小值的用处所在。队列里面不可能出现4,因为自己减自己没意义。
代码:
#include <iostream>
#include <queue>
#include <stdio.h>
#include <string.h>
using namespace std;
const int maxn=100005;
const int inf=0x7fffffff;
int num[maxn];
int sum[maxn*2];
int n,k;
int main()
{
int t;scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
sum[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
sum[i]=sum[i-1]+num[i];
}
for(int i=1;i<k;i++)
sum[n+i]=sum[n+i-1]+num[i];
int temp=n;
n=n+k-1;
deque<int>q;
int ans=-inf;
int start,end;
for(int i=1;i<=n;i++)
{
while(!q.empty()&&sum[i-1]<sum[q.back()])//维护队列的单调递增,首元素为最小值
q.pop_back();
while(!q.empty()&&q.front()<i-k)
q.pop_front();//区间长度为K这个限制
q.push_back(i-1);
if(sum[i]-sum[q.front()]>ans)
{
ans=sum[i]-sum[q.front()];//ans为num[q.front()+1]+num[q.front()+2]+....num[i]的和
start=q.front()+1;
end=i;
}
}
printf("%d %d %d\n",ans,start,(end>temp?end%temp:end));
}
}