Noip2017普及组

T4 跳房子 二分答案 单调队列

跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
  跳房子的游戏规则如下:
  在地面上确定一个起点,然后在起点右侧画 n 个格子,这些格子都在同一条直线上。每个格子内有一个数字( 整数),表示到达这个格子能得到的分数。玩家第一次从起点开始向右跳, 跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。
  现在小 R 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 d。小 R 希望改进他的机器人,如果他花 g 个金币改进他的机器人,那么他的机器人灵活性就能增加 g, 但是需要注意的是,每次弹跳的距离至少为 1。 具体而言, 当g < d时, 他的机器人每次可以选择向右弹跳的距离为 d-g, d-g+1,d-g+2, …, d+g-2, d+g-1, d+g; 否则( 当g ≥ d时),他的机器人每次可以选择向右弹跳的距离为 1, 2, 3, …, d+g-2, d+g-1, d+g。
  现在小 R 希望获得至少 k 分,请问他至少要花多少金币来改造他的机器人。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
const int maxn=500005;
using namespace std;
//int n,d,k;
//int len[maxn],value[maxn],dp[maxn],que[maxn*2];
//(因为单调)二分+动态规划+(用单调队列优化) 
//我觉得单调队列最难理解
//当每次i加1后,会有上面部分j退出区间,也会有尾部的j加入区间,此时如果重新遍历区间求最大值就会浪费---所以维护一个单调队列
//包含区间内的部分j,使得对头的dp最大,队尾的dp最小,每次取队头的位最大值
//单调队列会自动过滤无用的j,保持dp[j]单调 
/*
int maxcost(int a){
	memset(dp,-127,sizeof(dp));
	int head=1,tail=0;
	int mi=max(1,d-a);
	int mx=d+a;
	int rt=0,fir=-1;
	que[++tail]=0,dp[0]=0;
	for(int i=1;i<=n;i++){
		if(len[i]<mi) continue;
		if(len[i]>=mi&&fir==-1) fir=i;
		if(len[i]-len[i-1]>mx) break; //过不去 
		while(len[i]-len[fir]>=mi&&fir<i){
			while(head<=tail&&dp[fir]>dp[que[tail]]) tail--;  //把无用的队尾丢掉 
			if(dp[fir]!=-0x7f7f7f7f) que[++tail]=fir;  //把对头的弄到队尾来? 
			fir++;
		}
		while(head<=tail&&len[que[head]]+mx<len[i]) head++;  //把无用的队头丢掉 
		if(head>tail) dp[i]=-0x7f7f7f7f;
		else dp[i]=dp[que[head]]+value[i];
		if(dp[i]>rt) rt=dp[i];
	}
	return rt;
}
*/
//上面是单调队列的写法,还可以用deque双端队列
//下面直接用动态规划也可以

long long f[maxn],a[maxn][2],n,d,k,lpos,rpos;
bool check(int g){
	lpos=d-g;rpos=d+g;
	if(lpos<=0) lpos=1;
	memset(f,-127,sizeof(f));//先把dp数组初始化为一个很小的值
	f[0]=0 ; //起点
	for(int i=1;i<=n;i++){
		for(int j=i-1;j>=0;j--){ //用前面的去优化后面的 
			if(a[i][0]-a[j][0]<lpos) continue; //太靠左
			if(a[i][0]-a[j][0]>rpos) break; //太靠右
			f[i]=max(f[j]+a[i][1],f[i]);
			if(f[i]>=k) return 1; 
		}
	} 
	return 0;
}

int main(){
	cin>>n>>d>>k;
	/*
	for(int i=1;i<=n;i++){
		cin>>len[i]>>value[i];
	}
	int left=0,right=len[n];
	while(left!=right){
		int mid=(left+right)>>1;
		if(maxcost(mid)>=k) right=mid;
		else left=mid+1;
	}
	if(maxcost(left)<k) cout<<"-1"<<endl;
	else cout<<left<<endl; 
	*/
	for(int i=1;i<=n;i++){
		cin>>a[i][0]>>a[i][1];
	}
	int ans=-1,l=0,r=20001,m=(l+r)/2;
	while(l<=r){
		if(check(m)){
			ans=m;
			r=m-1;
		}
		else l=m+1;
		m=(l+r)/2;
	}
	cout<<ans<<endl;
return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/shirlybaby/p/12241077.html