• [USACO18JAN]Lifeguards P 洛谷黑题,单调队列优化DP


    传送门:戳我

    这道题有两个版本,S和P,S是K等于1的情况,显然可以用线段树水过。

    P版本就难了很多,洛谷黑题(NOI/NOI+/CTSC),嘿嘿。

    我自己也不是很理解,照着题解写了一遍,然后悟到了一点东西。

    dp方程很好想:

    dp[i][j]表示处理到第i个元素,已经删掉了j个,但取了第i个。

    dp[i][j]=max(dp[k][j-i-k-1])。表示考虑上一个取的区间是k,然后删去的个数就是j-i-k-1。

    时间复杂度是O(N*K*K)。显然不满足题目数据(或许卡卡常能过?)

    那么考虑单调队列优化

    因为我自己也有点蒙圈,怕误导大家,就摘抄了其他巨佬的博客

    /*  这段内容摘自luogu用户babingbaboom的博客:https://www.luogu.org/blog/user51357/solution-p4182

    考虑优化dp转移

    对于第i个区间,设其左端点为l

    我们先看一看方程,会发现对dp[i][j]产生贡献的i'-j'=i-1-j

    1. 对于i之前的那些右端点<=l的区间,它们与i没有重叠部分,所以只要在它们当中取max,再加上第i个区间的长度即可

    2. 对于那些与i有重叠部分的区间,在当前区间右移的时候,这些dp的贡献会变,但相对大小不会变,所以可以维护一个单调队列,dp[i][j]对应的单调队列的编号为i-1-j,每次先把队头的那些已经跑到左边的区间弹出去(算成1的贡献),然后取队头就是当前的有重叠的状态中的最大答案

    然后当前dp值算出来以后要插进对应的单调队列中(编号为i-j的单调队列),如果队尾状态加上与当前状态的右端点差还没有当前状态的dp值大的话,就把它从队尾弹出

    */

    代码实现如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define maxn 100001
    using namespace std;
    inline void read(int &x)
    {
        x=0;int f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        x*=f;
    }
    int N,K,dp[maxn][110];
    int p[maxn];
    struct coww{
        int l,r;
        friend bool operator < (coww a,coww b)
        {
            if(a.l==b.l)
                return a.r>b.r;
            else
                return a.l<b.l;
        } 
    }arr[maxn],cow[maxn];
    struct node{
        int node,val;
    };
    deque<node>que[maxn];
    int main()
    {
        read(N);read(K);
        if (K>=N)
        {
            printf("0");
            return 0;
        }
        for(int i=1;i<=N;i++)    
        {
            read(arr[i].l);
            read(arr[i].r);
        }
        sort(arr+1,arr+1+N);
        int right=0,cnt=0;
        cow[0]=(coww){0,0};
        for(int i=1;i<=N;i++)
        {
            if(arr[i].r>right)
                cow[++cnt]=arr[i],right=arr[i].r;
            else    
                K--;
        }
        if(K<0) K=0;
        N=cnt;    
        for(int i=1;i<=N;i++)
        {
            int u=min(K+1,i);
            for(int j=0;j<u;j++)
            {
                int now=i-j-1;
                while(!que[now].empty()&&cow[que[now].front().node].r<cow[i].l)
                {
                    node to=que[now].front();
                    p[now]=max(p[now],to.val+cow[to.node].r);
                    que[now].pop_front();
                }
                dp[i][j]=max(dp[i][j],p[now]+cow[i].r-cow[i].l);
                if (!que[now].empty())
                      dp[i][j]=max(dp[i][j],que[now].front().val+cow[i].r);
                  int nowv=dp[i][j]-cow[i].r;
                      now=i-j;
                 while ((!que[now].empty())&&(que[now].back().val<nowv))
                 que[now].pop_back();
                  que[now].push_back((node){i,nowv});
            }
        }
        int ans=0;
         for (int i=1;i<=N;i++)
              for (int j=0;j<min(i,K+1);j++)
                  if (j+N-i==K)
                    ans=max(ans,dp[i][j]);
          printf("%d",ans);
    }
    View Code
  • 相关阅读:
    第九周学习总结&实验报告(7)
    团队展示
    结对编程
    微信公众号
    编程作业
    《构建之法》
    自我介绍
    java学期总结
    14周作业
    13周总结
  • 原文地址:https://www.cnblogs.com/sherrlock/p/9814507.html
Copyright © 2020-2023  润新知