题目描述
跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子的游戏规则如下:
在地面上确定一个起点,然后在起点右侧画 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 分,请问他至少要花多少金币来改造他的机器人。
这道题看到之后马上二分答案然后dp来check是否成立,但是如果直接dp的话就会有[max(1,d-g),g+d]*n*logn的复杂度最后五个点就会T。由于在dp过程中的每个区间都是固定的,就可以用区间滑块或者就是用单调队列进行优化。
附上ac代码
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; const int maxn=500005; int len[maxn],value[maxn],dp[maxn],que[maxn*2]; int n,d,k,maxlow; int mymax(int a,int b) { return a>b?a:b; } int maxcost(int a) { memset(dp,-127,sizeof(dp)); int head=1,tail=0; int mi=mymax(d-a,1); 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 ; } int main() { freopen("jump.in","r",stdin); freopen("jump.out","w",stdout); scanf("%d%d%d",&n,&d,&k); for (int i=1;i<=n;i++) { scanf("%d",&len[i]); scanf("%d",&value[i]); } int lf=0; int rg=len[n]; while (lf!=rg) { int mid=(lf+rg)>>1; if (maxcost(mid)>=k) rg=mid; else lf=mid+1; } if (maxcost(lf)<k) printf("-1\n"); else printf("%d\n",lf); return 0; }