P3957 跳房子(二分答案+单调队列优化DP)

题目链接: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\n",cost);
61     return 0;
62 }

 

posted @ 2017-11-15 19:10  Yeader  阅读(852)  评论(0编辑  收藏  举报