题目
# 问题描述
AB 实验同学每天都很苦恼如何可以更好地进行 AB 实验,每一步的流程很重要,我们目标为了缩短所需的步数。
我们假设每一步对应到每一个位置。从一个整数位置 `x` 走到另外一个整数位置 `y`,每一步的长度是正整数,每步的值等于上一步的值 `-1`, `+0`,`+1`。求 `x` 到 `y` 最少走几步。并且第一步必须是 `1`,最后一步必须是 `1`,从 `x` 到 `y` 最少需要多少步。
## 样例说明
- 整数位置 `x` 为 `12`,另外一个整数位置 `y` 为 `6`,我们需要从 `x` 走到 `y`,最小的步数为:`1`,`2`,`2`,`1`,所以我们需要走 `4` 步。
- 整数位置 `x` 为 `34`,另外一个整数位置 `y` 为 `45`,我们需要从 `x` 走到 `y`,最小的步数为:`1`,`2`,`3`,`2`,`2`,`1`,所以我们需要走 `6` 步。
- 整数位置 `x` 为 `50`,另外一个整数位置 `y` 为 `30`,我们需要从 `x` 走到 `y`,最小的步数为:`1`,`2`,`3`,`4`,`4`,`3`,`2`,`1`,所以我们需要走 `8` 步。## 输入格式
输入包含 `2` 个整数 `x`,`y`。(`0<=x<=y<2^31`)
## 输出格式
对于每一组数据,输出一行,仅包含一个整数,从 `x` 到 `y` 所需最小步数。
解题思路
刚开始我的想法是暴力模拟,设置变量表示步长、步数、剩余步数......,模拟半天最终放弃,这时候才看到样例也是巨大,无法模拟,那就只能琢磨聪明一点的方法了
题目中说第一步长度为1,且最后一步长度也为1,并且每次步长只能加一减一或是不变,按照贪心的想,想要所走步数最少,那么我的步长就要尽可能的大,所以最终的答案大体上是一个慢慢上升,又慢慢下降的序列,中间可能会夹杂着一些相等的数
假设最大步长为k,那么所走步数一定是先从1加到K,然后又从K减为1 ,类似上图
在答案中可能会有两步步长一样长的情况,所以这个序列真实情况不一定是对称的,为了方便计算我们现在只表示答案序列中的连续部分,假设答案中的所有数字和为diff(即起点终点之间的距离)
前面上升序列 (1+2+......+k)=k*(k+1)/2 后面的下降序列(k-1+.......+1)=k*(k-1)/2
都是公差为1的等差数列,那么答案序列中未被计算的长度 remain=diff - k*(k+1)/2 - k*(k-1)/2
目前已知步数就是 k(前半部分被记入的步数) 和 k-1(后半部分被记入的步数),接下来贪心的把剩余步数remain走完,我们已经有了一个连续的步数序列,并且相邻的步数的长度可以相同,那么剩余部分中大于最大步长k的部分我们就用k去走,循环减去k去走就可以了,然后剩余的小于k的距离我们一步就可以走完(将它插入到我们已经计算的连续等差数列中去)
例如题目中所给样例答案,`1`,`2`,`3`,`2`,`2`,`1`,利用上面的思路,其中最大的步长为3,我们摘出来上升数列和下降数列后,剩余距离为2,小于最大步长,所以可以直接塞入答案序列当中,一步就能完成
那么这个最大步长k是多少呢?最简单的方式直接从1开始枚举,最大不可能超过两点的距离。如果想要代码节省一点时间,我们可以想一下最大的步长会是多少呢?通过上面我们知道答案序列可以表示为 k*(k+1)/2 + k*(k-1)/2 +剩余距离,当剩余距离为0,左边的k才可能达到最大值,即 k*(k+1)/2 + k*(k-1)/2=diff,化简可以得到k*k=diff,即步长最大为根号diff,所以我们从1枚举到根号diff即可。
这题不能扣细节,要把有序的部分拿出来,处理好有序的数据后在处理杂乱无章的那些步数。下面上代码
代码
public class Main {
public static int solution(int xPosition, int yPosition) {
int start=Math.min(xPosition,yPosition);
int end=Math.max(yPosition,xPosition);
int diff=end-start;
int ans=Integer.MAX_VALUE;
//枚举步数的最大值
for(int i=1;i*i<=diff;i++){
int sum=f(i)+f(i-1);//计算既定的走过的长度
int remain=diff-sum;//剩余多少
int cnt=0;//剩余距离需要多少步
//按此种最大步长行走不妥则退出
if(remain<0){
break;
}
//如果剩余步数大于最大步长,则以最大步长行走
while(remain>i){
remain-=i;
cnt++;
}
//剩余步数大于0小于最大步长,可以一步走出
if(remain>0){
cnt++;
}
ans=Math.min(ans, cnt+2*i-1);
}
return ans;
}
public static int f(int x){
int k=x*(x+1)/2;
return k;
}
public static void main(String[] args) {
// You can add more test cases here
System.out.println(solution(12, 6) ==4);
System.out.println(solution(34, 45) ==6);
System.out.println(solution(50, 30) ==8);
}
}