P1868 饥饿的奶牛

题目链接P1868 饥饿的奶牛 - 洛谷 | 计算机科学教育新生态

题目描述

有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。

现用汉语翻译为:

有 N个区间,每个区间x,y表示提供的堆优质牧草。你可以选择任意区间但不能有重复的部分。

对于奶牛来说,自然是吃的越多越好,然而奶牛智商有限,现在请你帮助他。

输入格式

第一行一个整数 N。

接下来 N 行,每行两个数 x,y,描述一个区间。

输出格式

输出最多能吃到的牧草堆数。

输入输出样例

输入 1

3

1 3

7 8

3 4

输出 1

5

说明/提示

题意

有n个区间,每个区间的开头为x,结尾为y,其优质牧草的堆数为y-x+1

奶牛们要选其中的一些牧草,使所得的优质牧草最多

选择规则:所选的所有区间不能重叠(下标也不能重叠

思路

动态规划的第一步:对第一个或最后一个阶段进行分类讨论,将原问题分解为子问题

So,我们可以将每一个区间看做阶段,设为前i个阶段最多能吃到的最多优质牧草数

于是,状态转移方程就出来了

dp[i]=max(dp[i],dp[j]+(node[i].e-node[i].s+1));

其中枚举的是中所有区间中,结尾与第i个区间的开头最近的区间

注:所有输入的区间都需要提前按末端升序排列

这样每个区间都需要从枚举一次,其时间复杂度为

很显然,的做法会爆时间,那么该如何优化呢?

看一下代码,枚举的区间时耗费了很多时间

由于区间已经按末端升序排列,所以我们可以用二分查找来替代用for来枚举的区间

二分查找代码

int find(int i){
    int l=0,r=i,mid;//记得是l=0,r=i,谨防越界
    while(l+1<r){//l+1<r万能条件
        mid=(l+r)>>1;
        if(node[m].s>node[mid].e)l=mid;
        else r=mid;
    }
    return l;//答案为l
}

AC代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,down,dp[222222];
struct Node{
    int s,e;
}node[222222];
bool cmp(Node x,Node y){
    return x.e<y.e;
}
int find(int i){
    int l=0,r=i,mid;//记得是l=0,r=i,谨防越界
    while(l+1<r){//l+1<r万能条件
        mid=(l+r)>>1;
        if(node[m].s>node[mid].e)l=mid;
        else r=mid;
    }
    return l;//答案为l
}
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld%lld",&node[i].s,&node[i].e);
    sort(node+1,node+1+n,cmp);
    for(int i=1;i<=n;i++){
        dp[i]=max(dp[i-1],dp[find(i)]+(node[i].e-node[i].s+1));
        //状态转移方程,j变为find(i)
    }
    printf("%lld",dp[n]); 
    return 0;
}

总结

线性dp中,如果因多重循环导致时间超限

可以考虑使用二分查找来降低复杂度(谨记排序)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值