题目大意:
每次步长b与上一次步长差距不超过1,即可以是a-1、a、a+1,起始步长为1,询问想从一点到达另一点最少需要多少步。
做题思路:
如果想使步数最少,那么我们会希望步长尽可能快的增加。如果是单纯的纯递增与纯递减,这样可能会出现超过两点之间距离的情况,于是问题就变成了我们怎么处理可能存在的非递增or非递减(b=a)的情况。
在一开始我确实没想好怎么办,遂开始乱画图,于是发现对于给定步数,我可以求出它能走过的最远距离,这样问题就可以变成几步的最远距离大于等于两点之间的距离。
关于怎么注意到可以去求最远距离:
如图,对于给定步长x,那么中间可到达最大步长是(x+1)/2,x为奇数时有唯一最大步长,x为偶数时最大步长出现两次,步长覆盖面积即为可覆盖最大距离,稍微积分or瞪眼(doge)就可以得到最大可覆盖距离公式。//当时画的更潦草希望这个还算清晰点。
查询最小步长的过程比较暴力(因为个人感觉掘金数据范围还是比较可爱的),主要是对步数的一些讨论,略微有一点点小细节,主要是针对可能需要的最小步长的奇偶行进行讨论(感觉代码冗余的原因是第7行没处理好,给出了可能建议,因为当时写完有点烦躁不是很想尝试,回头再补),详细代码如下:
public static int solution(int x_position, int y_position) {
// Please write your code here
int det=Math.abs(y_position - x_position);//求两点间距离
if (det==0)
return 0;//如果直接为零直接退出,省事
int ans=0;
for ( ; ans*ans<=det; ans++);
ans--;//此时ans是无法覆盖该距离的最多步数的一半 或者 恰好可以覆盖该距离步数的一半
//因为笔者比较懒,有兴趣同学可以将第7行改为ans*ans<det试试能否优化下面的代码
if (ans*ans==det){
ans=ans*2-1;//第八行后者:纯递增纯递减,仅出现一次最大步长,最理想状况
}else{
if (ans*(ans+1)>=det)
ans*=2;//可能出现两次最大步长,最大步长为ans
else{
if ((ans+1)*(ans+1)>=det)
ans=ans*2+1;//可能出现一次最大步长,最大步长为ans+1
else
ans=ans*2+2;//可能出现两次最大步长,最大步长为ans+1
}
}
return ans;
}
思路优化
写完暴力代码后我开始想:那么问题转化成了几步的最远距离大于等于两点之间的距离,我是否可以利用二分?
答案是肯定的,这种寻找前驱or后继的题目简直二分经典。于是,开动!
需要注意的一点是:mid*mid的范围有概率会超过int范围,所以需要开long类型,但是二分在时间上的优化是相当可观的。//提交了一发去跑错误的测试点才注意到的这个问题,还是小看了掘金的数据范围,也是我太大意了。
public static int solution(int x_position, int y_position) {
// Please write your code here
int d=Math.abs(y_position - x_position);
if (d==0)
return 0;
long l=0, r=d, ans=d;
while (l<r){
long mid=(l+r+1)/2;
if (mid%2==1){
if ((mid/2+1)*(mid/2+1)>=d){
r=mid-1; ans=mid;
}else{
l=mid;
}
}else{
if ((mid/2)*(mid/2+1)>=d){
r=mid-1; ans=mid;
}else{
l=mid;
}
}
}
int ans_=(int)ans;
return ans_;
}
总结
时间复杂度方面,从变成了,还是很不错的优化的!这题又练了一遍二分,爽!