BZOJ2442: [Usaco2011 Open]修剪草坪

【传送门:BZOJ2442


简要题意:

  约翰让他的奶牛来修建草坪。他有N 头奶牛,第i 头奶牛的工作能力为Ai。编号相近的奶牛很 熟悉,如果同时让K + 1 头编号连在一起的奶牛工作,她们就会密谋罢工。请问,约翰应该让哪些奶 牛同时工作,使得它们的能力之和最大,而且不会罢工。


输入格式:

  • 第一行:两个整数N 和K,1 ≤ K ≤ N ≤ 10^5

  • 第二行到N + 1 行:第i + 1 行有一个整数Ai,1 ≤ Ai ≤ 10^9


输出格式:

  • 单个整数,表示在所有不会罢工的奶牛组合之中,最大的能力之和


样例输入:

5 2

1

2

3

4

5


样例输出:

12


样例解释:

  除了第三头以外的所有奶牛都工作,总能力 为1 + 2 + 4 + 5 = 12


题解:

  一开始想到用DP,以为可以AC,结果发现数据范围惊人,想到用单调队列或者斜率优化来搞搞

  想了好久没想出来,后来发现可以转化为每k+1个数就要选一个数,求最小和的经典单调队列例题,求出最小和之后,用所有数的和减去最小和就是这道题的正解了

  尴尬的是,被long long硬是卡了十几分钟......


参考代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long LL;
struct node
{
    LL x;int p;
    node()
    {
        x=0LL;
    }
}list[110000];
LL a[110000],f[110000];
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);k++;
    LL s=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        s+=a[i];
    }
    if(n<=k-1)
    {
        printf("%lld
",s);
        return 0;
    }
    int head=1,tail=1;
    LL ans=99999999999;
    for(int i=1;i<=n;i++)
    {
        while(head<=tail&&i-list[head].p>k) head++;
        f[i]=list[head].x+a[i];
        while(head<=tail&&f[i]<=list[tail].x) tail--;
        tail++;list[tail].x=f[i];list[tail].p=i;
    }
    for(int i=n-k+1;i<=n;i++) ans=min(ans,f[i]);
    printf("%lld
",s-ans);
    return 0;
}

 

原文地址:https://www.cnblogs.com/Never-mind/p/7778476.html