题目链接
分析:
我们发现,当我们花费a枚金币对机器人进行改造时,若能拿到最大数字和z,那么显然当我们花费b(b>a) 枚金币也必然能得到数字和z甚至更大,至少采用和花费a时相同的走法就能保证最大数字和不会降低。
所以我们决定采用二分的算法,理论上二分的上限为xi的大小,即109,但由于数据过水(虽然没给出)保证了相邻两点相距不超过1000,故设二分上限为1000即可,二分下限显然为0。
最大数字和=F(花费金币)这个函数不递减,所以我们可以采用二分法来查找最小花费金币数。
剩下的问题就是如何计算在确定花费的前提下计算出最大数字和(或者判断能否超过k)。
由于每次跳跃必须落到格子上,所以可以使用DP求解,dp[i] 可由递推式 dp[i] = max{ dp[i],max{ dp[j] } + s[i] }(dis( i , j ) 在步幅内) 来求出。
代码如下
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
bool check(LL x[],int s[],int n,int d,LL k,int cost)
{
int minn=d-cost,maxx=d+cost;
LL dp[n+1];
memset(dp,0x80,sizeof(dp));
dp[0]=0;
for(int i=1; i<=n; i++)
{
for(int j=i-1; j>=0; j--)
{
if(x[i]-x[j]<minn)continue;
if(x[i]-x[j]>maxx)break;
dp[i]=max(dp[i],dp[j]+s[i]);
}
//cout<<"dp["<<i<<"]="<<dp[i]<<endl;
if(dp[i]>=k)
return true;
}
return false;
}
int main()
{
//input
int n,d;
LL k;
cin>>n>>d>>k;
LL x[n+1];
int s[n+1];
x[0]=0;
for(int i=1; i<=n; i++)
cin>>x[i]>>s[i];
//binary search
int ans=-1;
//双开区间
int lt=-1,rt=1001;
while(lt<=rt-2)
{
int mid=(lt+rt)>>1;
if(check(x,s,n,d,k,mid))
{
//cout<<mid<<" is OK"<<endl;
ans=mid;
rt=mid;
}
else
{
//cout<<mid<<" is not OK"<<endl;
lt=mid;
}
}
//output
cout<<ans<<endl;
return 0;
}
题外话
虽说以上代码就能AC了,但其实是由于数据有隐含的特性,若数据按题目所述,二分则需要从 0到109 ,也就是代码需要改成
int lt=-1,rt=1000000001;
这样就会导致TLE。
所有求区间最值问题都可以用单调队列来优化,这题也不例外,优化为单调队列后的代码即使二分从 0到109 也不会TLE。
终极代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
bool check(LL x[],int s[],int n,int d,LL k,int cost)
{
int minn=d-cost,maxx=d+cost;
//monotone queue
LL mq[n+1];
int mq_id[n+1];
int ft=0,rr=0;//front,rear
LL dp[n+1];
memset(dp,0x80,sizeof(dp));
dp[0]=0;
int j=0;
for(int i=1; i<=n; i++)
{
while(j<i&&x[i]-x[j]>=minn)
{
while(ft<rr&&dp[j]>=mq[rr-1])rr--;
mq[rr]=dp[j];
mq_id[rr++]=j;
j++;
}
while(ft<rr&&x[i]-x[mq_id[ft]]>maxx)ft++;
if(ft<rr)dp[i]=max(dp[i],mq[ft]+s[i]);
//cout<<"dp["<<i<<"]="<<dp[i]<<endl;
if(dp[i]>=k)
return true;
}
return false;
}
int main()
{
//input
int n,d;
LL k;
cin>>n>>d>>k;
LL x[n+1];
int s[n+1];
x[0]=0;
for(int i=1; i<=n; i++)
cin>>x[i]>>s[i];
//binary search
int ans=-1;
//双开区间
int lt=-1,rt=1000000001;
while(lt<=rt-2)
{
int mid=(lt+rt)>>1;
if(check(x,s,n,d,k,mid))
{
//cout<<mid<<" is OK"<<endl;
ans=mid;
rt=mid;
}
else
{
//cout<<mid<<" is not OK"<<endl;
lt=mid;
}
}
//output
cout<<ans<<endl;
return 0;
}
分类
二分、DP、单调队列