AcWing1087.修剪草坪(单调队列DP)

题目传送门

在一年前赢得了小镇的最佳草坪比赛后,FJ 变得很懒,再也没有修剪过草坪。

现在,新一轮的最佳草坪比赛又开始了,FJ 希望能够再次夺冠。

然而,FJ 的草坪非常脏乱,因此,FJ 只能够让他的奶牛来完成这项工作。

FJ 有 N 只排成一排的奶牛,编号为 1 到 N。

每只奶牛的效率是不同的,奶牛 i 的效率为 Ei。

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

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

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

输入格式

第一行:空格隔开的两个整数 N 和 K;

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

输出格式

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

数据范围

1≤N≤105
0≤Ei≤109

输入样例:

5 2
1
2
3
4
5

输出样例:

12

样例解释

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

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

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

题解:

f[i]表示前 i 个人合法情况下的最高效率, s[i]为前缀和

则对于f[i]:

1.不选第 i 个人, f[i] = f[i - 1]

2.选第 i 个人, 设在 i 前面选了 x 个人, 则f[i] = max(f[i - x - 1] + s[i] - s[i - x]) = max(f[i - x - 1] - s[i - x]) + s[i];

3.由于数据范围很大, 所以我们可以用单调队列来维护f[i - x - 1] - s[i - x]的最大值,

#include<iostream>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, m;
ll f[N];
ll sum[N];
int st[N];
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> sum[i];
        sum[i] += sum[i - 1];
    }

    int l = 0, r = 0;
    for(int i = 1; i <= n; i++){
        if(i - st[l] > m && l <= r)l++;
        //不选第 i 个, 选第 i 个
        f[i] = max(f[i - 1], f[st[l] - 1] - sum[st[l]] + sum[i]);
        //维护f[i - x - 1] - s[i - x] 的单调递减行, 则队头的元素一定是最大的
        while(l <= r && f[st[r] - 1]  - sum[st[r]] <= f[i - 1] - sum[i])r--;
        st[++r] = i;
    }
    cout << f[n] << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43328040/article/details/106835285