传送门:跳房子
题意:机器人开始时在0的位置,总共有k个格子,每个格子的分数为,每个格子与原点的距离为
当花费g个金币后,机器人能跳的距离为,也就是说机器人跳的最短距离不为0,最短为d-g或1,要我们求花最少的金币g到达至少k分。
我们知道当g个金币能得到的分数,那g+1,g+2,······都能得到,因为当金币花费大时,能选的格子数就越多,得分可能就越多。
那我们怎样求这个最小金币的花费值g呢?
显然我们可以用动态规划解题,第i个格子的最大分数有,状态转移为第
个格子的最大得分等于第
个格子的最大得分加上第
个格子的得分。当然在限定距离下可能跳不到第
个格子,所以要注意判断条件。如果这样做,那我们的时间复杂度就为
.。
因为这个可转移的状态是可以递推的,也就是说假设有一个状态先无法转移到
,且状态
在
后面,那
状态就一定不能转移到
状态。这里的状态指的是第几个点,也就是第几个位置。
那么根据这个性质我们就可以考虑用单调队列来优化。
那我们要维护的区间就是
代表在第j个格子里能跳的距离。
其涵义为:在第j个格子能跳的最短距离和最大距离的之间的所有格子,让这些点加入到队列中,同时让此时的更新为
,即如果能跳到这个格子,且我要跳到这个格子,我当然得从之前得分最大的格子跳过来,以此类推后面每个格子都是最大得分。同时我们也要维护队列的单调性,维护队首为最大值,并保证队首在区间内,如果不在区间,则表示队首已经不能跳到后面的格子需要将队首弹出。
bool check(int g){
int pos=0,front=1,rear=0,l=max((long long)1,d-g),r=d+g;
for(int i=1;i<=n;i++){
/*如果当前位置加上可以跳的最短距离大于下一个点,
代表不可能跳到下一个点,反之则可能跳到下一个点 */
while(x[pos]+l<=x[i]){ //当找到一个可能跳上去的点
//维护单调递减队列使得队首为最大值
while(front<=rear&&f[que[rear]]=k)return true;//当前的g能拿到至少要的分数
}
return false; // 当前的g不能拿到至少要的分数
}
当输入为如下是 ,即n=7,d=4,k=10 测试g=2的情况 能跳的距离为
7 4 10
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2
![]() |
![]() |
队列![]() |
![]() |
![]() |
![]() |
2 | 6 | 0 | 1 | 0 | 6 |
5 | -3 | 6 | 2 | 1 | 6-3=3 |
10 | 3 | 3 | 3 | 2 | 3+3=6 |
11 | -3 | 6 3 | 4 | 3 | 3+-(3)=0 |
13 | 1 | 6 3 1 | 5 | 4 | 6+1=7 |
17 | 6 | 7 | 6 | 5 | 7+6=13 |
20 | 2 |
当然这个函数只能判断当金币为g时能不能得到至少k分,所以我们要找到这个g就要分治,如果花费g金币能获得k分,那么花更多金币就一定能获得至少k分,那我们就往小的方向找
,
,否则如果花g金币不能得到这个分数,那我们必须花更多金币,也就往
方向找,直到找到花费金币数量最小的g。
此时我们的时间花费为。
#include
using namespace std;
long long n,d,k,que[5000005],f[5000005],x[5000005],s[5000005];
bool check(int g){
int pos=0,front=1,rear=0,l=max((long long)1,d-g),r=d+g;
for(int i=1;i<=n;i++){
/*如果当前位置加上可以跳的最短距离大于下一个点,
代表不可能跳到下一个点,反之则可能跳到下一个点 */
while(x[pos]+l<=x[i]){ //当找到一个可能跳上去的点
//维护单调递减队列使得队首为最大值
while(front<=rear&&f[que[rear]]=k)return true;//当前的g能拿到至少要的分数
}
return false; // 当前的g不能拿到至少要的分数
}
int main(){
cin>>n>>d>>k;
for(int i=1;i<=n;i++)
cin>>x[i]>>s[i];
int L=1,R=x[n],mid,ans;
while(L
附上结果: