跳房子

好题笔记

跳房子

这一题是一可以说是二分答案的一个经典应用,想来讲一下不加优化的版本,也就是可以的一半的分。首先我们看到了最少的g,那么我们是不是就想到了二分答案,我们找出它可以到达的左端点(最开始赋值为1),之后找出可以到达的右边最大值(注意这里是max(d,a[n].z),因为有可能最开始就一下跳出去了),然后二分他,mid=(l+r)>>1
之后就开始判断,只要(l,mid)可以那么我们一定就去找更小的,如果不可以我们去找最大的,至于判断也很简单,我们发现肯定这一个格子是从上一个取得得分最大值的点跳过来的,那么用一个dp来记录没跳过得分就标记为负无穷,dp[0]=0,一波动态规划就欧克了。
这个简易版本的代码如下

#include<bits/stdc++.h>
using namespace std;
const long long neInf=0x8080808080808080;
struct ge{
	int w;
	int z;
};
ge a[500005];
int n,d,k;
int lr=1,rl;
long long f[500005];
bool check(int l,int r){//进来的这个值是终点
	memset(f,0x80,sizeof(f));
	f[0]=0;//最开始的方案是一步都不跳 
	for(int i=1;i<=n;i++){
		for(int j=0;j<i;j++){
			if(a[i].w-a[j].w>=l&&a[i].w-a[j].w<=r&&f[j]!=0x80)
				f[i]=max(f[i],f[j]+a[i].z);//我要跳这个格子肯定从得分最高的的跳来 
		}
		if(f[i]>=k) return true;
	}
	return false;
	
}


int main(){
	scanf("%d%d%d",&n,&d,&k);
	long long ans=0;
	for(int i=1;i<=n;i++){
		scanf("%d%d",&a[i].w,&a[i].z);
		if(a[i].z>0) ans+=a[i].z;
		rl=max(d,a[i].w);
	}
	if(ans<k) {
		printf("-1");
		return 0;
	}
	ans=0;
	a[0].z=0;
	a[0].w=0;
	while(lr<rl){
		int lcan;
		int rcan;
		int mid=(lr+rl)/2;
		//cout<<mid<<"  ";
		if(d-mid<1) lcan=1;
		else if(d-mid>=1) lcan=d-mid;
		rcan=mid+d;
		//cout<<rcan<<"   "<<lcan<<"   ";
		if(check(lcan,rcan)){
			ans=mid;
			rl=mid;
		//	cout<<ans<<endl;
		}
		else {
			lr=mid+1;
		}
		
	}
	printf("%lld",ans);
	return 0;
}

但是我们可以发现动态规划里有可以优化的东西,最先想到的肯定是记忆化搜索,但是我想了一想感觉难以下手,之后我就想到了优先队列这东东,因为我们每一次维护前一个最大值点都是符合一个一个区间的,那么我们完全可以用一个单调队列去维护这个区间最大值。
其实我们知道在dp枚举每一个点是后出来的点的dp值如果比前面的大那么我们完全没有必要去每一次找j前面那些点,所以我们用一个数组q去存单调对列里面都是哪些下表,每一次去维护他的递减性,并且发现队首的距离太远时去弹出队首,并每一次特判一下这个点到底可不可以到达,如果可以我们就把他在下一次查找i+1时入队,并且去判断前面那些的情况,如果无法到达,那么我们就不用去更改他的dp值
最后代码如下

	#include<bits/stdc++.h>
	using namespace std;
	const long long neInf=0x8080808080808080;
	struct ge{
		int w;
		int z;
	};
	ge a[500005];
	int n,d,k;
	int lr=1,rl;
	long long f[500005];
	int q[500005];//存单调队列;里面都是那些书的下标 
	bool check(int l,int r){//进来的这个值是终点
		memset(f,0x80,sizeof(f));
		memset(q,0,sizeof(q));
		f[0]=0;//最开始的方案是一步都不跳 
		int head=1;
		int last=0; 
		int j=0; //这里j不用每一次都更新,因为我们下一次就是会从上一个的基础上去找,同时我们还要利用j去吧j-1给入队
		for(int i=1;i<=n;i++){
			while(a[i].w-a[j].w>=l&&i>j){
				if(f[j]!=0x80){
					while(last>=head&&f[q[last]]<=f[j]) last--;//比这个点还要小的肯定就没用了,这些小的肯定在j之前,那么我之后肯定也就不用这些了,出队就OK 
					q[++last]=j;//把j入队 
				}
				j++;
			}
			while(a[i].w-a[q[head]].w>r&&head<=last) head++;//如果超出了就去掉 
			if(head<=last) f[i]=f[q[head]]+a[i].z;
			if(f[i]>=k) return true;
		}
		return false;
		
	}
	
	
	int main(){
		scanf("%d%d%d",&n,&d,&k);
		long long ans=0;
		for(int i=1;i<=n;i++){
			scanf("%d%d",&a[i].w,&a[i].z);
			if(a[i].z>0) ans+=a[i].z;
			rl=max(d,a[i].w);
		}
		if(ans<k) {
			printf("-1");
			return 0;
		}
		ans=0;
		a[0].z=0;
		a[0].w=0;
		while(lr<rl){
			int lcan;
			int rcan;
			int mid=(lr+rl)/2;
			if(d-mid<1) lcan=1;
			else if(d-mid>=1) lcan=d-mid;
			rcan=mid+d;
			if(check(lcan,rcan)){
				ans=mid;
				rl=mid;
			}
			else {
				lr=mid+1;
			}
			
		}
		printf("%lld",ans);
		return 0;
	}
JavaScript是一种脚本语言,常用于网页开发。在跳房子1游戏中,我们可以利用JavaScript实现玩家与电脑之间的互动和游戏逻辑。 首先,我们可以通过JavaScript来创建游戏的画布,并在画布上绘制游戏地图。游戏地图可以使用HTML的 \<canvas> 元素来创建,并使用JavaScript的绘图API来绘制不同的游戏元素,例如房子、玩家和障碍物。 接着,我们可以使用JavaScript来处理玩家的输入。通过监听玩家的键盘事件,我们可以实现玩家在游戏地图上移动的功能。当玩家按下相应的键时,JavaScript可以更新玩家角色的位置,并检查是否发生碰撞,比如玩家是否撞到了墙壁或障碍物。 在游戏逻辑方面,JavaScript可以实现跳跃的功能。我们可以使用JavaScript的定时器功能,在一段时间间隔内,让玩家角色在画布上跳跃。当玩家按下跳跃键时,JavaScript可以记录玩家的跳跃状态,并在每一帧更新玩家的垂直位置,使其看起来像是在地图上跳跃。 除此之外,我们还可以通过JavaScript来实现游戏的计分系统。当玩家成功跳过一个房子时,JavaScript可以更新玩家的得分,并将得分显示在游戏界面上。我们还可以在JavaScript中设定游戏结束的条件,比如玩家撞到墙壁或障碍物,当条件满足时,JavaScript可以展示玩家的最终得分并重置游戏。 通过以上的方式,我们可以用JavaScript实现跳房子1游戏,使玩家能够与游戏进行互动,并在游戏中体验到挑战和乐趣。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值