SCU-3968 Mowing the Lawn(DP+单调队列)

题意

n 个非负整数,选若干个数保证连续的数不超过 k 个,求选的数总和最大是多少。
1 n 100000

思路

为了方便考虑问题,我们把题干转化为选若干个数,且间隔的数不超过 k 个,求选的数总和最小是多少。
d p i 为最后一个选的数为 i 时的最小总和,按照题设不难列出方程:
d p i = m i n { d p j } + a i j [ i k 1 , i 1 ]
考虑用单调队列优化,不难发现,只要在枚举 i 时,实时维护 d p i k 1 d p i 1 的最小值即可,这可以用单调队列实现。

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define N 100003
typedef long long LL;
using namespace std;
LL E[N],dp[N];

bool cmp1(LL x,LL y){return x<y;}
template<LL *a,bool cmp(LL,LL)>struct monoque
{                 //单调队列模板,对于a数组的,队头到队尾单调性为cmp的单调队列,队头是L
    int q[N],L,R;
    monoque(){L=1,R=0;}
    void clear(){L=1,R=0;}
    void del(int _){while(L<=R&&!cmp(a[q[R]],a[_]))R--;}
    void push(int _){q[++R]=_;}
    int front(){return q[L];}
    int rear(){return q[R];}
    void pop(int _){if(_>=q[L])L++;}
};
monoque<dp,cmp1>mq;

int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k))
    {
        LL sum=0,ans=1e18;
        FOR(i,1,n)
        {
            scanf("%d",&E[i]);
            sum+=E[i];
        }
        E[n+1]=0;
        dp[0]=0;
        mq.clear();
        mq.push(0);
        FOR(i,1,n+1)
        {
            dp[i]=dp[mq.front()]+E[i];
            mq.del(i);
            mq.push(i);
            mq.pop(i-k-1);
        }
        printf("%lld\n",sum-dp[n+1]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Paulliant/article/details/81164444
今日推荐