zoukankan      html  css  js  c++  java
  • bzoj2442[Usaco2011 Open]修剪草坪——单调队列优化

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2442

    考虑记录前 i 个、末尾 j 个连续选上的最大值。发现时空会爆。

    又发现大量的转移形如 dp[ i ][ j ] = dp[ i-1 ][ j-1 ]+a[ i ]。

      再结合自己求答案要遍历 j = i ~ j - k ,就觉得可以只记录一个 i ,在 i 到 i - k 的范围强制选后面连续的一段,并让转移来的dp的后面一个强制不选。

    这样在 i 到 i-k 的范围里在强制选的后缀之前一定有一个不选的,符合条件。

    实现需要一点技巧。

      1.dp[i-1]表示 i-1 之后的那个格子不选。所以应该先fx+=a[i],再dp[i-1]-=fx,这样dp[i-1]没有加上a[i]的值,符合设定。

      2.如果从开始就一直选,岂不是要从dp[ -1 ]转移来才行?所以把所有角标+1,就行了!原来的dp[0]变成dp[1],不用特殊管。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define ll long long
    using namespace std;
    const int N=1e5+5;
    int n,k,q[N];
    ll a[N],dp[N],h,t,fx;
    int rdn()
    {
        int ret=0;char ch=getchar();
        while(ch>'9'||ch<'0')ch=getchar();
        while(ch>='0'&&ch<='9')(ret*=10)+=ch-'0',ch=getchar();
        return ret;
    }
    ll rdl()
    {
        ll ret=0;char ch=getchar();
        while(ch>'9'||ch<'0')ch=getchar();
        while(ch>='0'&&ch<='9')(ret*=10)+=ch-'0',ch=getchar();
        return ret;
    }
    int main()
    {
        n=rdn();k=rdn();
        h=1;q[++t]=0;
        for(int i=2;i<=n+1;i++)
        {
            a[i]=rdl();
            while(h<=t&&i-q[h]>k+1)h++;
            fx+=a[i];dp[i-1]-=fx;
            while(h<=t&&dp[i-1]+fx>=dp[q[t]]+fx)t--;
            q[++t]=i-1;
            dp[i]=dp[q[h]]+fx;
    //        printf("dp[%d]=%lld dp[%d]=%lld fx=%lld
    ",i,dp[i],q[h],dp[q[h]],fx);
        }
        printf("%lld",dp[n+1]);
        return 0;
    }
  • 相关阅读:
    洛谷P1043数字游戏
    luogu P1330 封锁阳光大学
    luoguP1242 新汉诺塔
    luogu P1892 [BOI2003]团伙
    luogu P3375 【模板】KMP字符串匹配
    luoguP1440 求m区间内的最小值
    luoguP2700 逐个击破
    luoguP2814 家谱
    luogu P1962 斐波那契数列
    P3379 【模板】最近公共祖先(LCA)
  • 原文地址:https://www.cnblogs.com/Narh/p/9179586.html
Copyright © 2011-2022 走看看