题目链接:https://www.luogu.org/contestnew/show/4468
题目大意:跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子的游戏规则如下:
在地面上确定一个起点,然后在起点右侧画 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题,然后可以总结为以下几点:
①g的枚举范围很大,可以用二分枚举。
②dp[i]表示在第i个格子上的最大得分,score[i]表示第i个格子上的分值,dis[i]表示第i个格子离出发点的距离,可得到状态转移方程:dp[i]=max{dp[j]+score[i]}(j<i,max(d-g,1)<=dis[i]-dis[j]<=d+g)
③按上述方法,复杂度依旧到达了n^2lg(M_g),可以知道,dp[i]是跟距离成正比的,可以用单调队列优化,最后复杂度为O(nlg(M_g))。
④还犯了一个小错误,就是默认起点可以到达第一个格子了,O(≧口≦)O。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 typedef long long LL; 7 const int N=5e5+5; 8 const long long INF=4e18; 9 10 int n,d,k; 11 LL dis[N],score[N],dp[N],q[N]; 12 13 bool judge(int i,int j,int g){ 14 if(dis[i]-dis[j]<=g+d&&dis[i]-dis[j]>=max(d-g,1)) 15 return true; 16 return false; 17 } 18 19 bool check(int g){ 20 //错误:默认了0可以到达第一个点 21 int head=0,tail=0,cur=0,qmin=max(d-g,1),qmax=d+g; 22 q[tail++]=0; 23 for(int i=1;i<=n;i++){ 24 while(head+1<tail&&dis[i]-dis[q[head]]>qmax){ 25 head++; 26 } 27 if(judge(i,q[head],g)) 28 dp[i]=dp[q[head]]+score[i]; 29 else 30 dp[i]=-INF; 31 if(dp[i]>=k) 32 return true; 33 while(cur<=i&&dis[i+1]-dis[cur]>=qmin){ 34 //能到达q[tail-1]的肯定能到达cur点,且dp[cur]价值较大,所以q[tail-1]点可以舍去 35 while(head+1<=tail&&dp[q[tail-1]]<=dp[cur]){ 36 tail--; 37 } 38 q[tail++]=cur++; 39 } 40 } 41 return false; 42 } 43 44 int main(){ 45 scanf("%d%d%d",&n,&d,&k); 46 for(int i=1;i<=n;i++){ 47 scanf("%lld%lld",&dis[i],&score[i]); 48 } 49 int l=0,r=1e9,cost=-1; 50 while(l<=r){ 51 int mid=(l+r)/2; 52 if(check(mid)){ 53 if(cost==-1||cost>mid) 54 cost=mid; 55 r=mid-1; 56 } 57 else 58 l=mid+1; 59 } 60 printf("%d ",cost); 61 return 0; 62 }