计算位置X到Y的最少步数-算法题解

题目

# 问题描述

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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值