可以计算出在O(1)时间内在无限棋盘上从(0,0)移至(A,B)的骑士所需的移动次数。

这是尝试在O(n)时间内完成的解决方案,其中n是所需的移动次数。

,否则,则返回-1。

我感兴趣的内容:


据我所知,它在O(n)中运行时间。但是,我可能不正确。是吗?
是否有一种方法可以改进我的算法,使其在O(n)时间内运行?


评论

我们只考虑在这个无限的棋盘上选一个骑士,对吗?这就是我所假设的,因为与其他国际象棋相撞是不合法的。

@ syb0rg是的,只是骑士。

在什么条件下骑士不可能进入广场?

“有可能计算出在O(1)时间内无限制棋盘上一个骑士从(0,0)移至(A,B)所需的移动次数。”这句话是错误的。证明非常简单:输出的大小不是恒定的。如果您在无限棋盘中选择越来越大的A和B,则移动的次数会增加,因此输出的大小也会增加。计算此类移动次数n的时间至少为O(log(n)),否则算法甚至没有时间产生输出。

骑士始终可以移动到任何广场,这绝不是不可能的,因为骑士无法到达任何广场:例如参见骑士之旅。

#1 楼

\ $ O(1)\ $讨论


是否可以计算\ $ O(1)\ $时间复杂度中的距离?


是的。

这个问题被称为“骑士距离”问题,通过谷歌搜索找到了许多参考文献。

这个问题是由于南非“计算机奥林匹克竞赛”于2007年为高中生解决....(我知道,这些天孩子们.... :))。该规范使得程序的某些输入值足够大,使得\ $ O(n)\ $解决方案无法在所需的时间限制内进行计算,因此要获得(设计和)计算\所需的满分。 $ O(1)\ $系统。

问题说明以及\ $ O(1)\ $解决方案的足够说明。万一.za网络死了,这是解决方案的描述:


4 Ni的骑士

(x,y)坐标50%的约束很小,足以生成一个完整的网格,其中包含从任意点到(0,0)所需的最小移动次数。可以通过广度优先
搜索(BFS)生成网格。

我们知道到达(0,0)需要0步。因此,我们知道
点(1,2),(2,1)和其他六个运动每个都需要运动1
。从最短距离1的每个点中,我们可以
在每个方向上采取另一步骤以最短路径2。但是,
一些动作会使我们回到已经找到的点a
距离较短,因此我们放弃了这种移动。我们继续
直到网格填满,然后使用网格找到每个骑士的距离。

要获得100%的打印效率,需要在纸上进行很多涂鸦进行一些关键的观察并制定一些公式。首先要注意的是
,网格沿x,y轴和线y = ±x是对称的。
因此,您可以将所有点(x, y)转换为等效点
,以便新的0 ≤ y ≤ x

神奇的公式是:

$$
f( x,y)= \ left \ {
\ begin {array} {ll}
2 \ left \ lfloor \ frac {y-\ delta} {3} \ right \ rfloor + \ delta&\ mbox {if} y> \ delta \\
\ delta-2 \ left \ lfloor \ frac {\ delta-y} {4} \ right \ rfloor和\ mbox {otherwise}
\ end {数组}
\右。
$$

其中\ $δ= x − y \ $。


是否存在错误

是的。...骑士可以进入所有广场。为什么会有限制?

我将您的代码放入一个Ideone中,该Ideone循环遍历位置(0,0)到(9,9)的100个目标...,结果是令人失望...您的代码是否有效?

否。它不是。它产生非常错误的值.....

现在,我上面引用的算法似乎也没有产生好的值(至少是短距离....),但是认真吗?您的代码几乎不起作用:(

很多,很多-1距离,很多0 ......

评论


\ $ \ begingroup \ $
找到了O(1)时间的解决方案。 chessprogramming.wikispaces.com/骑士距离
\ $ \ endgroup \ $
–安德里斯
2014年1月30日7:01

\ $ \ begingroup \ $
对于x = 1和y = 0,该公式给出1,这怎么可能?
\ $ \ endgroup \ $
– Krystian Lieber
16年1月8日在12:29

\ $ \ begingroup \ $
公式似乎只适用于不需要反向运动的较大输入。但是,我仍然想知道他们是如何解决的。
\ $ \ endgroup \ $
–奥利夫
2016年12月9日23:56

\ $ \ begingroup \ $
这个公式有一个小错误,要在这里正确看一下stackoverflow.com/a/41704071/827753
\ $ \ endgroup \ $
–西蒙
17年1月18日在13:18

#2 楼

一些注意事项(如您所说,除了效率):



您定义的ABS带有圆括号,这不是C程序员声明宏的常规方法,因此它们不是'也有问题。

#define ABS(a) do { typeof (a) _a = (a); _a > 0 ? _a : -_a; } while(0)


在这种情况下,也没有必要使用typeof。只需使用abs()中的<math.h>函数,或定义一个更简单的ABS宏即可。

#define ABS(x) ((x) > 0 ? x : -x)


请注意,必须使用fabs()来查找浮点的绝对值使用<math.h>库输入数字。

为什么所有的单个字母变量名称都大写?

您需要让一些代码喘口气以使其更易于阅读(在我看来)。

X += distX > 0 ? 2 : -2;



使用更多注释来说明您的代码做什么以及为什么。不过请不要过度使用。

if (!(((A-X) % 2) ^ ((Y-B) % 2)))  //<insert description here>




评论


\ $ \ begingroup \ $
关于最后一点,为了易于阅读,应该将操作数和运算符分开。
\ $ \ endgroup \ $
– Jamal♦
2014年1月29日22:15

\ $ \ begingroup \ $
谢谢您的回答syb0rg。我同意第一个和最后一个建议。使用大写名称仅是因为规范中的A和B大写。
\ $ \ endgroup \ $
–最大
2014年1月29日在22:25

\ $ \ begingroup \ $
@Alec很好。变量名以小写字母开头更常见,所以我想知道为什么它是大写的。
\ $ \ endgroup \ $
–syb0rg
2014年1月29日在22:26

\ $ \ begingroup \ $
我希望在前一行而不是内联上发表评论。
\ $ \ endgroup \ $
– ChristW
2014年1月29日23:19

\ $ \ begingroup \ $
@ChrisW根据评论的持续时间,可以。如果这是简短描述,很容易放在同一行上,那么我通常将它们放在同一行上。
\ $ \ endgroup \ $
–syb0rg
2014年1月29日23:20

#3 楼

我对此有一点建议:

#define ABS(a) ({ typeof (a) _a = (a); _a > 0 ? _a : -_a; })


首先,typeof是gcc扩展,因此通过使用它,您使代码的可移植性降低了。公平的说,对于像这样的小事情来说,这不是一个大问题,但要养成一个坏习惯。在这种情况下,甚至也不需要它。

#define ABS(x) ((x) > 0 ? x : -x)


也可以正常工作。 。 <math.h>为您定义abs(用于整数)和fabs(用于浮点值)。除非有很好的理由,否则请使用标准库。

评论


\ $ \ begingroup \ $
另一方面,如果您使用支持typeof的编译器(例如GCC或Clang),则此ABS(x)宏避免两次对其参数进行求值。
\ $ \ endgroup \ $
– 200_success
2014年1月30日6:52