【牛客基础】数学考试(前缀和+线性DP)

链接: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 
#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<<"====="<
        }
//        for(int i=1;i<=n;i++)
//        {
//            cout<
//        }
        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;
}

你可能感兴趣的:(牛客基础)