2019.10.04日常总结兼洛谷P3957(跳房子)题解

【地址链接】: 题目地址:https://www.luogu.org/problem/P3957
【思路】:
( 1 ) . (1). 1. 无解情况:所有 ≥ 0 \geq 0 0的分数加起来仍然 < k <k,无解,输出 − 1 -1 1
( 2 ) . (2). 2. 对于有解情况,我们发现,答案的下界为 0 0 0,答案的上界为 m a x { d , max\{d, max{d, n n n个格子的位置 } \} },且答案满足单调性,所以,我们很容易想到二分
( 3 ) . (3). 3.二分花费 m i d mid mid,则跳跃的长度 i i i 满足 m a x ( 1 , d − m i d ) ≤ i ≤ d + m i d max(1,d-mid) \leq i \leq d+mid max(1,dmid)id+mid,考虑如何进行判定,即 c h e c k ( ) check() check() 的实现
( 4 ) . (4). 4.使用动归求,记 f i f_i fi 表示以 i i i格为结尾的分数最大值,我们可以发现 f i = m a x { f j } + i ( m a x ( 1 , d − m i d ) ≤ i − j ≤ d + m i d ) f_i=max \{f_j\}+i(max(1,d-mid) \leq i-j \leq d+mid) fi=max{fj}+i(max(1,dmid)ijd+mid)的分数
( 5 ) . (5). 5. 我们发现普通 d p dp dp的时间复杂度很高,所以我们考虑优化。我们发现区间长度是一定的,所以我们可以使用单调队列进行优化,数据复杂度被降得很低
【代码】:

//By HPXXZYY
#include 
using namespace std;
#define gc getchar()
#define g(c) isdigit(c)
inline long long read(){
	char c=0;long long x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}//长仅仅6行,但威力无穷的快读  
const int N=500100;
struct node{
	int sub;long long point;
	void read_itself(){
		sub=read();point=read();
	}
}a[N];long long f[N];
int q[N],le,ri,n,d,k;
const long long inf=0x8080808080808080;
inline bool check(int Left,int Right){
	memset(q,0,sizeof(q));
	memset(f,0x80,sizeof(f));
//	初始化dp数组和单调队列的过程  
	le=1;ri=0;int j=0;f[0]=0;
	for(int i=1;i<=n;i++){
		while (a[i].sub-a[j].sub>=Left&&j<i){
			if (f[j]!=inf){
				while (le<=ri&&f[q[ri]]<=f[j]) ri--;
//				及时剔除单调队列中不优的答案  
				q[++ri]=j;//把当前最优答案放入单调队列  
			}
			++j;//别忘了它  
		}
		while (le<=ri&&a[i].sub-a[q[le]].sub>Right) le++;
//		及时把单调队列中过时的答案剔除  
		if (le<=ri) f[i]=f[q[le]]+a[i].point;
//		利用单调队列中的最优解更新答案  
	}
	for(int i=1;i<=n;i++)
	if (f[i]>=k) return true;
	return false;
}
int maxn,minn,i;long long sum;
//注意sum一定要是long long类型的
inline int binary_search(int minn,int maxn){
	int l=minn,r=maxn,mid,ans;
	while (l<=r){
		mid=(l+r)>>1;
		int minv,maxv;
		minv=max(1,d-mid);
		maxv=d+mid;
		if (check(minv,maxv)){
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	return ans;
}//笔者最推荐的二分写法  
int main(){
//	freopen("t1.in","r",stdin);
	n=read();d=read();k=read();
	for(i=1;i<=n;i++){
		a[i].read_itself();
		if (a[i].point>0)
		sum+=a[i].point;
//		sum:期望最高得分 
	}
	if (sum<k) cout<<-1;
	else{
		minn=0;maxn=max(d,a[n].sub);
		cout<<binary_search(minn,maxn);
	}
	return 0;
}

安利一下我的洛谷博客:https://www.luogu.org/blog/hpwwzyy2012/

你可能感兴趣的:(原创)