洛谷P1868 饥饿的奶牛【DP】

本文介绍了一道关于选择不重叠区间以最大化牧草数量的问题,并提供了一种基于动态规划和二分查找的高效解决方案。通过将区间按右端点排序并利用dp数组来跟踪最优解,该算法能在O(n log n)的时间复杂度内解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

时空限制 1000ms / 128MB

题目描述

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

输入格式:

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

输出格式:

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

说明

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


题目分析

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

那么有dp转移
dp[i]=max(dp[i−1], Ri−Li+1, max(dp[j]+Ri−Li+1))(1&lt;=j&lt;i, Rj&lt;Li)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)

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

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


#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、付费专栏及课程。

余额充值