T4 跳房子 二分答案 单调队列
跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子的游戏规则如下:
在地面上确定一个起点,然后在起点右侧画 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 分,请问他至少要花多少金币来改造他的机器人。
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
const int maxn=500005;
using namespace std;
//int n,d,k;
//int len[maxn],value[maxn],dp[maxn],que[maxn*2];
//(因为单调)二分+动态规划+(用单调队列优化)
//我觉得单调队列最难理解
//当每次i加1后,会有上面部分j退出区间,也会有尾部的j加入区间,此时如果重新遍历区间求最大值就会浪费---所以维护一个单调队列
//包含区间内的部分j,使得对头的dp最大,队尾的dp最小,每次取队头的位最大值
//单调队列会自动过滤无用的j,保持dp[j]单调
/*
int maxcost(int a){
memset(dp,-127,sizeof(dp));
int head=1,tail=0;
int mi=max(1,d-a);
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;
}
*/
//上面是单调队列的写法,还可以用deque双端队列
//下面直接用动态规划也可以
long long f[maxn],a[maxn][2],n,d,k,lpos,rpos;
bool check(int g){
lpos=d-g;rpos=d+g;
if(lpos<=0) lpos=1;
memset(f,-127,sizeof(f));//先把dp数组初始化为一个很小的值
f[0]=0 ; //起点
for(int i=1;i<=n;i++){
for(int j=i-1;j>=0;j--){ //用前面的去优化后面的
if(a[i][0]-a[j][0]<lpos) continue; //太靠左
if(a[i][0]-a[j][0]>rpos) break; //太靠右
f[i]=max(f[j]+a[i][1],f[i]);
if(f[i]>=k) return 1;
}
}
return 0;
}
int main(){
cin>>n>>d>>k;
/*
for(int i=1;i<=n;i++){
cin>>len[i]>>value[i];
}
int left=0,right=len[n];
while(left!=right){
int mid=(left+right)>>1;
if(maxcost(mid)>=k) right=mid;
else left=mid+1;
}
if(maxcost(left)<k) cout<<"-1"<<endl;
else cout<<left<<endl;
*/
for(int i=1;i<=n;i++){
cin>>a[i][0]>>a[i][1];
}
int ans=-1,l=0,r=20001,m=(l+r)/2;
while(l<=r){
if(check(m)){
ans=m;
r=m-1;
}
else l=m+1;
m=(l+r)/2;
}
cout<<ans<<endl;
return 0;
}
原文:https://www.cnblogs.com/shirlybaby/p/12241077.html