[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

 

posted @ 2018-10-19 09:13  溡沭  阅读(783)  评论(0编辑  收藏  举报