链接:https://ac.nowcoder.com/acm/problem/15553
来源:牛客网
题目描述
今天qwb要参加一个数学考试,这套试卷一共有n道题,每道题qwb能获得的分数为ai,qwb并不打算把这些题全做完,
他想选总共2k道题来做,并且期望他能获得的分数尽可能的大,他准备选2个不连续的长度为k的区间,
即[L,L+1,L+2,…,L+k-1],[R,R+1,R+2,…,R+k-1](R >= L+k)。
输入描述:
第一行一个整数T(T<=10),代表有T组数据
接下来一行两个整数n,k,(1<=n<=200,000),(1<=k,2k <= n)
接下来一行n个整数a1,a2,…,an,(-100,000<=ai<=100,000)
输出描述:
输出一个整数,qwb能获得的最大分数
示例1
输入
2
6 3
1 1 1 1 1 1
8 2
-1 0 2 -1 -1 2 3 -1
输出
6
7
【想说的】:这个题目结合了前缀和和简单线性DP,首先求一个区间的和这个很容易想到用前缀数组去做,题目有一点要注意,就是这两个长度为k的区间是不能相交的。所以想到,
(界限前面的)
设一个数sum1(sum从k到n-k),如果我以这个数为界限的话,这个数之前的区间里面(【1~sum1】)长度为k的区间的最大值是不是便利一下 max(dpl[sum1-1],带上sum1的区间),
以此类推,
(界限后面)
设一个sum2(sum2从n-k+到k+1)的界限,也是求带上它会不会更大一点,把结果存到dpr中。
最后整个遍历,前加后(注意一个是i 一个是i+1,不能重区间),get最大值就是答案
【收获】关于坐标的问题真的不要昏头,是在不太清除就写一个具体数列模拟一下。前缀和很常见了,单纯前缀和真不大可能,所以继续努力!
题解
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll f[2000010];
ll ff[200010];
ll dpl[200010];
ll dpr[200010];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(ff,0,sizeof(ff));
memset(dpl,-0x3f3f3f,sizeof(dpl));
memset(dpr,-0x3f3f3f,sizeof(dpr));
ll n,k;
scanf("%lld%lld",&n,&k);
f[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&f[i]);
ff[i]=ff[i-1]+f[i];
//cout<<"====="<<f[i]<<endl;
}
// for(int i=1;i<=n;i++)
// {
// cout<<ff[i]<<endl;
// }
for(int i=k;i<=n-k;i++)
{
dpl[i]=max(dpl[i-1],ff[i]-ff[i-k]);
}
for(int i=n-k+1;i>=k+1;i--)
{
dpr[i]=max(dpr[i+1],ff[i+k-1]-ff[i-1]);
}
ll ans=-1e18;
for(int i=k;i<=n-k;i++)
{
ans=max(ans,dpl[i]+dpr[i+1]);
}
cout<<ans<<endl;
}
return 0;
}