题目链接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个区间的开头最近的区间
注:所有输入的区间都需要提前按末端升序排列
![](https://i-blog.csdnimg.cn/blog_migrate/ad2630ab0d07b296f2e0a75aa84eeb5b.png)
这样每个区间都需要从枚举一次,其时间复杂度为
很显然,的做法会爆时间,那么该如何优化呢?
看一下代码,枚举的区间时耗费了很多时间
由于区间已经按末端升序排列,所以我们可以用二分查找来替代用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中,如果因多重循环导致时间超限
可以考虑使用二分查找来降低复杂度(谨记排序)