c++ - 仅使用两个操作遍历二叉树以从一个数字到另一个数字
问题描述
我正在做一个问题,说我们必须以尽可能少的步骤从一个数字n到另一个数字m ,其中每个“步骤”可以是 1) 加倍,或 2) 减一。自然的方法是两个构建二叉树并运行 BFS,因为给定n,m的范围为0 ≤ n,m ≤ 10 4,因此树不会变得那么大。但是,我遇到了一个非常简短的解决方案,并且不知道它为什么会起作用。它基本上从m ... n开始,而是根据需要减半或加一以减少m直到它小于n,然后才加起来直到n。这是代码:
while(n<m){
if (m%2) m++;
else m /= 2;
count++;
}
count = count + n - m;
return count;
为什么这一定是最短路径很明显吗?我从m ... n得到这个是很自然的,因为n的下界为零,所以树在某种意义上变得更加“有限”,但是这种修改减半的方法直到你低于这个数字,然后加起来直到你达到它,似乎它不一定总是返回正确的答案,但确实如此。为什么,以及我如何从一开始就认识到这种方法?
解决方案
您只有 2 个可用的操作:
- 双倍的
n
- 减去 1
n
这意味着上升的唯一方法是加倍,下降的唯一方法是减 1。
如果m
是一个偶数,那么你可以通过加倍n
when来获得它2*n = m
。否则,您也必须减去 1(如果2*n = m + 1
那样,您必须先加倍n
然后再减去 1)。
如果加倍n
落在上面太远,m
那么你将不得不减去两倍于在加倍之前使用减法的次数n
。
例如:
n = 12
和m = 20
。
您可以加倍n
然后减去 4 倍,如12*2 -4 = 20
. - 5 个步骤
或者您可以减去两次,然后加倍n
,如(12-2)*2 = 20
. - 3 个步骤
您可能想知道“我应该如何在加倍或减法之间进行选择?n < m/2
”。
这个想法是使用基于递归的方法。您知道您想要n
达到或之v
类的值。换句话说,您想要达到...,而做到这一点的最短方法是达到诸如or之类的值。v = m/2
v = (m+1)/2
n
v
v'
v' = v/2
v' = (v+1)/2
例如:
n = 2
和m = 21
。
你想n
达到(21+1)/2 = 11
,这意味着你想达到(11+1)/2 = 6
,从而达到6/2=3
,从而达到(3+1)/2 = 2
。
既然n=2
您现在知道最短路径是:(((n*2-1)*2)*2-1)*2-1
.
其他例子:
n = 14
和m = 22
。
你想n
达到22/2 = 11
。
n
已经在 11 以上,所以最短路径是 : (n-1-1-1)*2
。
从这里可以看出,不用二叉树也可以推导出最短路径。m
最重要的是,您必须考虑从n
. m
这意味着编写从到的算法n
比相反的更容易。
使用递归,这个函数实现了相同的结果:
function shortest(n, m) {
if (n >= m) return n-m; //only way to go down
if(m%2==0) return 1 + shortest(n, m/2); //if m is even => optimum goal is m/2
else return 2 + shortest(n, (m+1)/2);//else optimum goal is (m+1)/2 which necessitates 2 operations
}
推荐阅读
- python - 检查端子颜色
- javafx - javafx:在 Platform.runLater 中使用 StageStyle.TRANSPARENT 进行弹出和断点设置时的奇怪效果
- javascript - 如何使用语言国家代码相应地自定义货币符号?
- sql - 使用 sqlPackage.exe 部署 dacpac 时仅包括 SP、视图、表和函数
- r - 在 Plotly 和 R 中以红色显示底片
- svn - 将现有存储库添加到 svn
- xamarin.forms - 多平台库项目中的依赖服务?
- matlab - 在 Simulink 中绘制植物传递函数的问题
- android - 材质库(1.1.0-alpha08版)BottomNavigationView的menuItem如何显示徽章?
- wpf - 将静态项填充到 c# WPF 组合框中