修剪草坪(单调队列优化DP)

109 阅读2分钟

题目描述

FJ 有 NN 只排成一排的奶牛,编号为 11 到 NN

每只奶牛的效率是不同的,奶牛 ii 的效率为 EiE_i

编号相邻的奶牛们很熟悉,如果 FJ 安排超过 KK 只编号连续的奶牛,那么这些奶牛就会罢工去开派对。

因此,现在 FJ 需要你的帮助,找到最合理的安排方案并计算 FJ 可以得到的最大效率。

注意,方案需满足不能包含超过 KK 只编号连续的奶牛。

输入格式

第一行:空格隔开的两个整数 NN 和 KK

第二到 N+1N+1 行:第 i+1i+1 行有一个整数 EiE_i

输出格式

共一行,包含一个数值,表示 FJ 可以得到的最大的效率值。

数据范围

1N1051≤N≤10^5,
0Ei1090≤E_i≤10^9

输入样例:

5 2
1
2
3
4
5

输出样例:

12

样例解释

FJ 有 5 只奶牛,效率分别为 1、2、3、4、5。

FJ 希望选取的奶牛效率总和最大,但是他不能选取超过 2 只连续的奶牛。

因此可以选择第三只以外的其他奶牛,总的效率为 1 + 2 + 4 + 5 = 12。

题目分析

这是一道 单调队列优化DP 的问题。

像上题一样,本题需要多次求取区间和,则我们先将原数列转化为前缀和数列。

我们定义 f[i]f[i] 表示前 ii 头奶牛中,满足限制的奶牛的最大效率。

对于第 ii 头奶牛,能与其产生关系的是下标为 im+1i1i-m+1\sim i-1 的奶牛。我们假设从第 ii 头奶牛开始,连续向前选择了 k(0km)k(0\le k\le m) 头奶牛,并且下标为 iki-k 的奶牛不选,对于当前方案,其 f[i]=f[ik1]+s[i]s[ik]f[i]=f[i-k-1]+s[i]-s[i-k]

f[i]=max{f[ik1]+s[i]s[ik]}  (0km)f[i]=max\{f[i-k-1]+s[i]-s[i-k]\}\;(0\le k\le m)

s[i]s[i] 提出,得 f[i]=s[i]+max(f[ik1]s[ik])  (0km)f[i]=s[i]+max(f[i-k-1]-s[i-k])\;(0\le k\le m)

于是我们便可以利用单调队列维护上述 f[ik1]s[ik]f[i-k-1]-s[i-k] 的最大值啦。

由于对于第一头牛而言,需要其前两个下标有效,则奶牛从 22 开始编号。

Accept代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 100010;

int q[N], s[N], f[N];
int hh, tt = -1;
int n, m;

signed main()
{
    cin >> n >> m;
    for (int i = 2; i <= n + 1; i ++) cin >> s[i], s[i] += s[i - 1];
    q[++ tt] = 1;
    for (int i = 2; i <= n + 1; i ++)
    {
        if (q[hh] < i - m) hh ++;
        while (hh <= tt && f[i - 1] - s[i] >= f[q[tt] - 1] - s[q[tt]]) tt --;
        q[++ tt] = i;
        f[i] = s[i] + f[q[hh] - 1] - s[q[hh]];
    }
    cout << f[n + 1];
    return 0;
}