[洛谷P2627] 修剪草坪

 洛谷题号:P2627

出处: ?

主要算法:dp+单调队列优化

难度:4.3

思路分析:

      Dp还是容易看出来的。

  我的第一感觉是一维,f[i]表示前i头奶牛的最大效率。其实这也是可以解的,具体方法将会在后文介绍。

  考虑二维的解法,f[i][0]表示奶牛i不参与时的最大效率,f[i][1]表示奶牛i参与。我们知道,在前k头奶牛中必定有一头奶牛不参与——对于f[i][0]转移很简单,由于奶牛i不参与,一定是选择继承,所以必定有f[i][0] = Max(f[i-1][0], f[i-1][1]). 而f[i][1]可以利用f[i-j][0](0<j<K)来转移:f[i][1] = Max(f[i-j][0] + sum[i] - sum[i-j])

  这个方程的意思就是i-j这头奶牛不选,并继承最优子结构f[i-j][0],然后i-j之后的奶牛全部选择,于是用一个前缀和来维护即可。整理方程发现,sum[i]是确定的,于是可以将它提出max之外,得到f[i][1] = Max(f[i-j][0] - sum[i-j]) + sum[i],我们发现这个方程就只与i-j有关了,并且是个定长区间的最大值——很容易让我们联想到滑动窗口问题,于是通过单调队列来解决就好了。

  下面的代码贴的是以上之中方法……


  刚才我们提到了可以用一维来解决,即f[i]表示前i头奶牛的最大效率。其实是与二维一模一样的,二维实在是多此一举。由于i-j根本不选,我们可以直接继承f[i-j-1],在加上前缀和,就有了方程f[i][1] = Max(f[i-j-1] - sum[i-j]) + sum[i].

 

代码注意点:

  long long

/*By QiXingzhi*/
#include <cstdio>
#define  N  (100010)
#define  INF   (0x3f3f3f3f)
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
#define  r read()
typedef long long ll;
#define int ll
using namespace std;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar();
    return x * w;
}
int n,k,h=1,t;
int a[N],f[N][2],q[N],s[N];
inline void Push(int w){
    while(h<=t && f[w][0]-s[w] > f[q[t]][0]-s[q[t]]) --t;
    q[++t] = w;
}
#undef int
int main(){
//    freopen(".in","r",stdin);
    #define int ll
    n=r,k=r;
    for(int i = 1; i <= n; ++i){
        a[i]=r;
        s[i] = s[i-1]+a[i];
    } 
    f[1][0] = 0;
    f[1][1] = a[1];
    Push(0);
    Push(1);
    for(int i = 2; i <= n; ++i){
        f[i][0] = Max(f[i-1][0], f[i-1][1]);
        while(h<=t && q[h] < i-k) ++h;
        f[i][1] = f[q[h]][0] + s[i] - s[q[h]];
        Push(i);
    }
    printf("%lld",Max(f[n][0],f[n][1]));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qixingzhi/p/9260398.html