洛谷P1868 饥饿的奶牛【DP】

时空限制 1000ms / 128MB

题目描述

有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。
现用汉语翻译为:
有N个区间,每个区间x,y表示提供的x~y共y-x+1堆优质牧草。你可以选择任意区间但不能有重复的部分。
对于奶牛来说,自然是吃的越多越好,然而奶牛智商有限,现在请你帮助他。

输入格式:

第一行,N,如题
接下来N行,每行一个数x,y,如题

输出格式:

一个数,最多能吃到的牧草堆数

说明

1<=n<=150000
0<=x<=y<=3000000


题目分析

先将所有草堆以右端点为第一关键字排序
d p [ i ] dp[i] dp[i]表示考虑前 i i i堆草能获得的最大数量

那么有dp转移
d p [ i ] = m a x ( d p [ i − 1 ] ,   R i − L i + 1 ,   m a x ( d p [ j ] + R i − L i + 1 ) ) ( 1 &lt; = j &lt; i ,   R j &lt; L i ) dp[i]=max(dp[i-1],\ R_i-L_i+1,\ max(dp[j]+R_i-L_i+1))(1&lt;=j&lt;i,\ R_j&lt;L_i) dp[i]=max(dp[i1], RiLi+1, max(dp[j]+RiLi+1))(1<=j<i, Rj<Li)

显然 d p [ i ] dp[i] dp[i]一定是不下降的
若不存在 j j j满足 ( 1 &lt; = j &lt; i ,   R j &lt; L i ) (1&lt;=j&lt;i,\ R_j&lt;L_i) (1<=j<i, Rj<Li),则 d p [ i ] = m a x ( d p [ i − 1 ] ,   R i − L i + 1 ) dp[i]=max(dp[i-1],\ R_i-L_i+1) dp[i]=max(dp[i1], RiLi+1)
也就是要么不选第 i i i堆,要么只选第 i i i

而如果存在这样的 j j j
直接枚举 j j j去更新是 O ( n 2 ) O(n^2) O(n2)的,肯定T
但注意到 d p [ i ] dp[i] dp[i]不下降性
所以直接二分寻找满足 1 &lt; = j &lt; i 1&lt;=j&lt;i 1<=j<i i i i最近 j j j即可


#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=200010;
int n;
struct node{int ll,rr;}rem[maxn];
bool cmp(node a,node b){return a.rr==b.rr?a.ll<b.ll:a.rr<b.rr;}
int dp[maxn];

int find(int x)
{
    int ll=1,rr=x,mid,ans=-1;
    while(ll<rr)
    {
        mid=ll+rr>>1;
        if(rem[mid].rr<rem[x].ll) ans=mid,ll=mid+1;
        else rr=mid;
    }
    return ans;
}

int main()
{
    n=read();
    for(int i=1;i<=n;++i)
    rem[i].ll=read(),rem[i].rr=read();
    sort(rem+1,rem+1+n,cmp);
    
    dp[1]=rem[1].rr-rem[1].ll+1;
    for(int i=2;i<=n;++i)
    {
        dp[i]=max(dp[i-1],rem[i].rr-rem[i].ll+1); 
        int j=find(i);
        if(j!=-1) dp[i]=max(dp[i],dp[j]+rem[i].rr-rem[i].ll+1);
    }
    printf("%d",dp[n]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值