P1868 饥饿的奶牛 线性DP

posted on 2023-02-23 12:29:03 | under 题集 | source

题目简介

https://www.luogu.com.cn/problem/P1868

思路

这是一道区间问题,基本思路有两种,一种是以位置为阶段,一种是以每个区间为阶段。

最容易想到的方法是以区间为阶段。

首先对每个区间进行排序,这里是以结尾升序排列。

然后是状态:$ dp[i] $ 表示 i 个区间取的最大值。

接着想想怎么得到 $ dp[i] $ ,不妨设第 j 个区间的末尾小于第 i 个区间的开头,则 $ dp[i]$ 可以由 \(dp[j]\) 得到。

这样做乍一看时间复杂度是 \(n^2\) ,但可以优化。

由于已经对结尾进行了升序排列,且 \(dp[i]\) 的定义是前 i 个区间的最大值。

因此,只需利用二分查找,找到第一个满足条件的 j 。

再次提一下 \(dp[i]\) 的定义,上述方式是取第 i 个区间得到的最大值,可能不取它所得到值更大,所以需要对两种情况取最大值。

这样时间复杂度变为 $nlogn $ 。

附上代码:

#include<bits/stdc++.h>
using namespace std;
struct hh
{
	long long x,y,s;
}a[200005];
long long n,dp[200005]; 
bool cmp(hh a,hh b) {return a.y<b.y;}
int erf(int p)
{
	int l=0,r=p,mid;
	while(l+1<r)
	{
		mid=(l+r)>>1;
		if(a[mid].y<a[p].x) l=mid;
		else r=mid;
	}
	return l;
}
int main()
{
	cin>>n;
	for(int i=1; i<=n;i++) 
	{
		scanf("%lld%lld",&a[i].x,&a[i].y);
		a[i].s=a[i].y-a[i].x+1;
	}
	sort(a+1,a+1+n,cmp);
	for(int i=1; i<=n;i++)
	{
		dp[i]=max(dp[i-1],dp[erf(i)]+a[i].s);
	}
	cout<<dp[n];
	return 0;
 } 

总结

区间问题可以令区间为阶段,也可以令位置为阶段,哪个好做用哪个。

对于一些 DP 问题,可以考虑二分优化。

posted @ 2026-01-14 17:55  Zwi  阅读(0)  评论(0)    收藏  举报