牛客oj NC15553 数学考试

题目大意:

给你n个数,从中找出两段长度为k的无重合子列(n>2*k),使得这两段子列和最大。

n<=2e5 ;1s

 

朴素的想法:

暴力枚举两个子列的左/右端点,时间复杂度O(N^2),TLE

 

优化方法:

由于题目要求子列的和,我们容易想到求整个数列前i项的前缀和

记前一段以i结尾的子列和 first[i],后一段以j结尾的子列和 second[j]

(首先易得  k<=i<=n-k , i+k<=j<=n)

让我们以求first[i]为例,当已经求出一段子列的和之后,若要求下一段,不需要重新计算一整段,只需要比较first[i]与pre[i]-pre[i-k],取较大值即可

时间复杂度O(N)

 

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t,n,k;
ll a[200005],pre[200005],first[200005],second[200005];

int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&k);
pre[0]=0;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
pre[i]=pre[i-1]+a[i];
first[i]=-1e18,second[i]=-1e18;
}
first[k]=pre[k];
for(int i=k+1;i<=n-k;i++){
first[i]=max(first[i-1],pre[i]-pre[i-k]); //以i结束最大的子段
// cout<<i<<" "<<first[i]<<endl;
}
second[n]=pre[n]-pre[n-k];
for(int j=n-1;j>=2*k;j--){
second[j]=max(second[j+1],pre[j]-pre[j-k]);
// cout<<j<<" "<<second[j]<<endl;
}
ll ans=-1e18;
for(int i=k;i<=n-k;i++){
ans=max(ans,first[i]+second[i+k]);
}
printf("%lld\n",ans);
}
return 0;
}

posted @ 2021-06-10 20:11  starlightlmy  阅读(88)  评论(0)    收藏  举报