HDU-3415 Max Sum of Max-K-sub-sequence
题目大意
给你一个环形数组 a a a,求长度不超过 k k k的连续非空子段和的最大值以及这个子段的开始位置和结束位置。如果有多个结果,则输出起始位置最小的答案。如果还有多个结果,则输出长度最小的答案。
有 t t t组数据。
1 ≤ t ≤ 100 , 1 ≤ n ≤ 100000 , 1 ≤ k ≤ n , − 1000 ≤ a i ≤ 1000 1\leq t\leq 100,1\leq n\leq 100000,1\leq k\leq n,-1000\leq a_i\leq 1000 1≤t≤100,1≤n≤100000,1≤k≤n,−1000≤ai≤1000
题解
首先,把数组 a a a复制一份,放在 a a a后面,破环为链。
然后,我们求 a [ i ] a[i] a[i]的前缀和 s u m [ i ] sum[i] sum[i]。遍历一遍数组,当前遍历到的位置 i i i视为子段的结束位置。设以 i i i为结束位置的能使子段和最大的开始位置为 j + 1 j+1 j+1,因为子段和为 s u m i − s u m j sum_i-sum_j sumi−sumj,所以只要知道在 [ i − k , i − 1 ] [i-k,i-1] [i−k,i−1]上最小的 s u m j sum_j sumj,即可得到 j j j,这样就能得到开始位置 j + 1 j+1 j+1。用单调队列来维护区间最小前缀和,即可 O ( n ) O(n) O(n)处理每一次询问。
总时间复杂度为 O ( ∑ n ) O(\sum n) O(∑n)。
code
#include<iostream>
#include<cstdio>
using namespace std;
int t,n,k,hd,tl,ans,p1,p2,a[200005],sum[200005],q[200005];
int main()
{
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);a[n+i]=a[i];
}
for(int i=1;i<=2*n;i++){
sum[i]=sum[i-1]+a[i];
}
hd=1;tl=1;q[1]=0;
ans=-1e9;p1=p2=0;
for(int i=1;i<=2*n;i++){
while(q[hd]<i-k) ++hd;
if(sum[i]-sum[q[hd]]>ans){
ans=sum[i]-sum[q[hd]];
p1=q[hd]+1;p2=i;
}
while(hd<=tl&&sum[q[tl]]>=sum[i]) --tl;
q[++tl]=i;
}
if(p2>n) p2-=n;
printf("%d %d %d\n",ans,p1,p2);
}
return 0;
}
该文章介绍了一个算法问题,涉及环形数组和子序列最大和的寻找。通过将环形数组扩展为链,计算前缀和,使用单调队列维护最小前缀和,可以在O(n)的时间复杂度内求解每个询问的答案。最终,文章提供了一个C++代码示例来实现这一算法。

740

被折叠的 条评论
为什么被折叠?



