[luogu 3957] [2017 普及组]跳房子 {单调队列优化dp}

版权声明:~~~感谢支持! https://blog.csdn.net/qq_39897867/article/details/88927648

题目

https://www.luogu.org/problemnew/show/P3957


解题思路

我们可以先二分答案。
然后,设 f [ i ] f[i] 表示到第i个时的最优解,方程易得 f [ i ] = max { f [ i ] , f [ j ] + a [ i ] } f[i]=\max \left \{ f[i],f[j]+a[i] \right\}
但是对于 f [ j ] f[j] 可以用单调队列优化一下就可以了。

注意在维护单调队列的单调性的时候,注意条件(找了我将近一个小时的bug)


代码

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std; 
const int N=500010; 
int n,m,s,dis[N],a[N],l,r=N,tot,mid;
long long f[N],maxn; 
bool dp(int k){
	int L=m-k,R=m+k;  
	memset(f,0xcf,sizeof(f));
	f[0]=0; 
	deque<int>q; 
	tot=0;
	maxn=-1e16; 
	for (int i=1;i<=n;i++){
		while (q.size()&&dis[i]-dis[q.front()]>R) q.pop_front(); 
		while (dis[i]-dis[tot]>R&&i>tot) tot++; 
		while (dis[i]-dis[tot]>=L&&i>tot) {
			while (q.size()&&f[q.back()]<f[tot]) q.pop_back(); 
			q.push_back(tot); tot++; 
		}
		if (q.size()) f[i]=f[q.front()]+(long long)a[i]; 
		if (f[i]>maxn) maxn=f[i]; 
	}
	return maxn>=(long long)s; 
}
int main(){
	scanf("%d%d%d",&n,&m,&s); 
	for (int i=1;i<=n;i++) 
	     scanf("%d%d",&dis[i],&a[i]);
	while(l<=r){
		mid=(l+r)/2; 
		if (dp(mid)) r=mid-1; else l=mid+1; 	
	} 
	if (l>500000) printf("-1"); else printf("%d",r+1); 
}

猜你喜欢

转载自blog.csdn.net/qq_39897867/article/details/88927648