魔法 (美团笔试第四题)
题目描述
你是魔法魔法学院的一名学生,你在魔法课中因为老师讲得无聊开了小差。
你手中有两个数字 A 和 B,你可以对这两个数字释放以下三种魔法:
- 选择一个数字,让它变为两倍
- 选择一个数字,让它变为原来对一半后,向下取整。 如 5 -> 2 , 10 -> 5
- 选择一个数字,让它的值加 1。即 n = n + 1
你可以任意次的使用上述三种魔法。但要保证使用尽可能少的魔法使用次数使两个数 A 和 B 相等
输入描述
第一行: A B 待操作的两个数,以空格隔开
其中 0 ≤ A ≤ 1000 , 0 ≤ B ≤ 1000 0\le A\le1000 , 0\le B\le1000 0≤A≤1000,0≤B≤1000
样例输出
输入: 5 24
输出: 3
输入:70 80
输出: 7
输入 :4 7
输出:2
题目分析
简化描述
这题的描述很复杂,说白了就是给你两个数,三种操作方法。
每次操作其中一个数,返回使两个数相等的最少操作次数。
核心思想
刚开始拿到这道题,可能会觉得不是很好下手。因为每个数字有 3 种操作,两个数字就是 6 种。
这样采用层序遍历会非常复杂,效率也不高。
但是其实每一次执行哪个操作,完全取决于两个数的值。所以可以考虑贪心算法!
贪心 : 当两个数相差较大时,使用操作 1 和 操作 2 会使两个数之间的距离更短
- 所以我们优先使用 *2 和 /2 操作 ,以使两个数之间距离更近
对于 80 和 70 如果都采用方法 3 ,需要 10 次 。
把 80 / 2 ,再将 70 / 2 ,操作两次,两者相差 5,答案等于 7。
- 当两个数相聚很近时,使用 *2 和 /2 操作 反而 还没有操作 3 快
对于 7 和 9,7 / 2 = 3 ,9 / 2 = 4。操作两步,两者相差 1 。得 3 步
而只进行操作 3 只需要 2 步
所以,我们将连续的操作 1 和操作 2 视为 1 种操作,操作 3 视为另一种操作。
我们取两种操作中步数更少的那一个
递归:每次的子问题是一样的,只是操作的数不同。反复操作,直到两数相等。
代码实现
import java.util.Scanner;
class Solution {
int operate(int A, int B, int step) {
if (A == B) return step; // 如果两个数 相等 直接返回 step
if (Math.abs(A - B) == 1) return step + 1; // 两个数相差 1 返回 step + 1
int large_number = Math.max(A, B);
int small_number = Math.min(A, B);
int method1 = Math.abs(large_number - small_number * 2); // 小数 乘以 2 得到的距离
int method2 = Math.abs(large_number / 2 - small_number); // 大数 除以 2 取整 得到的距离
if (method1 < method2) small_number *= 2; //选择差值更小的方法
else large_number /= 2;
// 取 法 1 (* 和 /)和 法 2 中更小的那一个
return Math.min(operate(small_number, large_number, step + 1), step + Math.abs(A - B));
}
public static void main(String[] args) {
Solution solution = new Solution();
Scanner scanner = new Scanner(System.in);
int n1 = scanner.nextInt();
int n2 = scanner.nextInt();
System.out.println(solution.operate(n1, n2, 0));
}
}