【NOIP2017】跳房子
这题我0分。
比赛时,我一眼出正解,哈哈,太水了!
这题不就是一个二分+DP+单调队列吗?
然而,细节决定成败。
我错了许多细节,就挂了。
我只考了0分。。。
首先,这题满足一个条件:
保证g变大后,如果原来满足条件,现在也会满足条件;而如果原来不满足条件,现在就有可能满足条件。
g变小后,如果原来满足条件,现在不一定会满足条件;而如果原来不满足条件,现在就一定不可能满足条件。
所以,我们可以用二分找出最合适的g的值。
已知,\({0\leq g\leq 10^9}\),且跳跃的范围是 Max(1,d-g) ~ d+g。
我们就可以列出状态转移方程了:$${F_i=max(F_j)+S_i\space\space\space(X_j+a\leq X_i \bigwedge X_j+b\geq X_i)}$$
其中,a为跳跃最短距离,b为跳跃的最长距离。
这样做的时间复杂度是\({O(log_210^9n^2)}\),很明显会时超50分。
所以,我们就要把DP优化一下了。
我们很容易发现,状态转移方程中,对于不同的i,\({max(F_j)}\)的值可能是一样的,但我们的程序却会从一个较大的区间  上一次最后一个 找到的位置+1 ~  i-1  中找 ,这就是程序中最耗时的地方。
怎么优化呢?
我们有多种优化方式,其中我推荐两种:大根堆,还有单调队列。大根堆码量大,而单调队列方便快捷,因此我比较喜欢用单调队列。
用\({queue_i}\)表示单调队列的第i个元素,用head表示单调队列中有效范围内的第一个元素的下标,用tail表示单调队列中有效范围内的最后一个元素的下标。
单调队列存的是元素的下标,即\({F_i}\),\({X_i}\)中的 i ,这样能方便判断。
由于这个单调队列是递减的(即第一个元素最大,第二个元素比第一个小,第三个比第二个小……最后一个是最小的),所以我们每次使用的最大值就是单调队列中有效范围内的第一个元素对应的值。
那么我们的状态转移方程就可以变成这个样子了:$${F_i=F_{queue_{head}}+S_i\space\space(X_{queue_{head}}+a \leq X_i \bigwedge X_{queue_{head}}+b \geq X_i)}$$
其中,a为跳跃最短距离,b为跳跃的最长距离。
这样用起来是很方便的,但是,重点来了——
怎样才能保持单调队列的单调性(使单调队列递减)和有效性(使\({(X_{queue_{head}}+a \leq X_i \bigwedge X_{queue_{head}}+b \geq X_i)}\))呢?
首先,我们每一次加入元素时,如果 (new是新加入的元素),也就是说这个样子(越高的值越大):

许多人都会认为要变成下面这个样子:

第i个柱子上面的数字是X[queue[i]]的值。
由于新加入单调队列的数,都是可以跳到第i个格子上的,即\({X_{new}+a\leq X_i\bigwedge X_{new}+b\geq X_i}\)
而X又是递增的,所以\({X_{new}+a\leq X_i\leq X_{i+1}\leq X_{i+2}\cdots X_n}\) 。
但是从5(\({X_{new}}\))这个位置出发,能跳到的最远距离绝对比4远,所以当5不能跳到某一个地方时,4也绝对跳不到那个位置。所以4就没用了。
因此我们可以把4删掉(即tail-1),最后再把5加入,变成下面这个样子:

有时候我们要删除很多元素,如下面这个例子:

变成
我们就要用一个while循环来删除F值小于等于F[new]的数。
但我们的queue[head]是会过期的(queue[head]跳不到第i个格子),这时我们的queue[head]就不能用了。
我们要删掉queue[head],怎么删掉呢?直接head+1就好了。
最后一点,建议同学们把不能到达的点的F赋值为-maxlongint!
#include<cstdio>
using namespace std;
#define maxlongint 1999999999
int f[500001],queue[500001],x[500001],s[500001];
int main()
{
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
	int n,d,k,l=0,r=1000000000,mid,i,j,t,ans=-1,maxx,minn,head,tail,last;
	bool bk,bz;
	scanf("%d%d%d",&n,&d,&k);
	for(i=1;i<=n;i++) scanf("%d%d",&x[i],&s[i]);
	l=0;r=1000000000;
	while(l<=r)
	{
		mid=(l+r)/2;
		maxx=d+mid;bk=false;bz=true;
		minn=d-mid;last=0;
		if(minn<1) minn=1;
		tail=head=1;
		queue[1]=0;
		for(i=1;i<=n;i++)
		{
			if(maxx>=x[i]&&minn<=x[i])
			{
				bz=false;
				break;
			}
		}
		if(bz)
		{
			l=mid+1;
			continue;
		}
		for(i=1;i<=n;i++)
		{
			f[i]=maxlongint;
			for(j=last+1;j<i;j++)
			{
				if(x[j]+minn>x[i]) break;
				if(x[j]+maxx<x[i]) continue;
				last=j;
				if(f[j]==maxlongint) continue;
				while(head<=tail&&f[queue[tail]]<=f[j]) queue[tail--]=0;
				queue[++tail]=j;
			}
			while(head<tail&&x[queue[head]]+maxx<x[i]) head++;
			if(x[queue[head]]+maxx<x[i]||x[queue[head]]+minn>x[i]) f[i]=maxlongint;
			else f[i]=f[queue[head]]+s[i];
			if(f[i]<maxlongint&&f[i]>=k)
			{
				bk=true;
				break;
			}
		}
		if(bk)
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	printf("%d\n",ans);
	return 0;
}
【NOIP2017】跳房子的更多相关文章
- [P3957][NOIP2017]跳房子 (DP+二分/队列?)
		看到GREED_VI大佬在打这题 我这个蒟蒻偷偷看一眼洛谷上目前普及难度里最难的一题 题目还是能看懂的,不想道路游戏那题,我完全不知道题目是什么意思…… GREED_VI大佬第一次用的是二分的思想,于 ... 
- luogu P3657 (NOIP2017) 跳房子(二分+DP+单调队列)
		题面 传送门 分析 显然答案有单调性,可以二分答案,设当前二分值为g,根据题意我们可以求出跳跃长度的范围[l,r] 考虑DP 子状态: dp[i]表示跳到第i个点时的最大和 状态转移方程 \(dp[i ... 
- $NOIp$普及组做题记录
		\([NOIp2014]\) 螺旋矩阵 \(Sol\) 直接模拟,一次走一整行或者一整列.复杂度\(O(n)\). \(Code\) #include<bits/stdc++.h> #de ... 
- [NOIP2017普及组]跳房子(二分,单调队列优化dp)
		[NOIP2017普及组]跳房子 题目描述 跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一. 跳房子的游戏规则如下: 在地面上确定一个起点,然后在起点右侧画 nn 个格子, ... 
- NOIP2017 PJ 跳房子 —— 单调队列优化DP
		题目描述 跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一.跳房子的游戏规则如下: 在地面上确定一个起点,然后在起点右侧画n个格子,这些格子都在同一条直线上.每个格子内有一个 ... 
- [NOIP2017] T4 跳房子  DP+二分
		Description 跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一.跳房子的游戏规则如下: 在地面上确定一个起点,然后在起点右侧画 n 个格子,这些格子都在同一条直线 ... 
- 洛谷P3957 跳房子(Noip2017普及组 T4)
		今天我们的考试就考到了这道题,在考场上就压根没有思路,我知道它是一道dp的题,但因为太弱还是写不出来. 下来评讲的时候知道了一些思路,是dp加上二分查找的方式,还能够用单调队列优化. 但看了网上的许多 ... 
- Luogu 3957 [NOIP2017]普及组 跳房子
		写了好久,感觉自己好菜,唉…… 首先发现这个$g$的取值具有单调性,可以想到二分答案,然后考虑用$dp$来检验,这样子可以写出朴素的转移方程: 设$f_i$表示以$i$结尾的最大价值,那么有$f_i ... 
- [NOIP2017普及]跳房子
		我太弱了... 单调队列优化DP+二分答案. #include <algorithm> #include <iostream> #include <cstdlib> ... 
随机推荐
- delphi中Tkbmmemtable数据转成SQL脚本
			unit UMemtableToSql; interface uses SysUtils, Classes, DB, kbmMemTable, Variants, Dialogs, SuperObje ... 
- 2019 7.6 T2 虫洞
			虫洞(conch) [题目描述] HZY 现在在数轴原点处,她想跑到 2000001 这个点上.听说各路 神犇暑假里都在健♂身,所有 HZY 也想不要只是简单地跑步,于是她 决定在这条数轴上造虫洞,具 ... 
- Java线程之Dump
			什么是线程dump Java Thread dump记录了线程在jvm中的执行信息,可以看成是线程活动的日志.Java线程转储文件有助于分析应用程序和死锁情况中的瓶颈. 如何获取线程转储文件 在这里, ... 
- JavaWeb-SpringSecurity在数据库中查询登陆用户
			系列博文 项目已上传至guthub 传送门 JavaWeb-SpringSecurity初认识 传送门 JavaWeb-SpringSecurity在数据库中查询登陆用户 传送门 JavaWeb-Sp ... 
- Android_(消息提示)多种使用Toast的消息提示
			Toast是一种提供给用户简介信息的视图,可以创建和显示消息,该视图以浮于应用程序之上的形式呈现给用户.因为它并不获得焦点,即使用户正在输入什么也不会受到影响. Toast目标是尽可能以不显眼的方式, ... 
- [CSP-S模拟测试]:地理课(并查集+线段树分治)
			题目传送门(内部题146) 输入格式 从$geography.in$读入数据. 第一行两个数$n,m$,表示有$n$个点,$m$个时刻.接下来$m$行每行三个数,要么是$1\ u\ v$,要么是$2\ ... 
- vue生命周期updated的触发时机之debug过程中发现的firefox问题
			现象描述: 断点位置1 谷歌debug的过程: 火狐debug的过程: 只要在改变数据之后有断点停顿,就会先去执行updated函数 断点位置2 此时火狐和谷歌是一样的效果,但是执行顺序是不一致的 谷 ... 
- leetcode 547朋友圈
			方法一:染色法 类似于岛屿的个数也可以用染色法:通过深度优先搜索来做 使用一个数组来表示当前朋友a是否已经包含到已经遍历的朋友圈中,遍历所有的朋友,如果当前朋友没有在已经访问的朋友圈中,即visite ... 
- C#类型转换类(通用类)
			// /// 类型转换类 /// 处理数据库获取字段为空的情况 /// public static class DBConvert { #reg ... 
- [go]os.Open方法源码
			file, err := os.Open("./buf.go") func Open(name string) (*File, error) { return OpenFile(n ... 
