这是一个Hackerrank问题。
巴拜站在N * M桌子的左上角(1,1)。该表有N行和M列。最初他面对的是正确的牢房。他以下列方式在桌子上移动:
- 他向前迈进了一步
- 他转向右边
- 如果前进让他退出桌子的边界,或者让他到达一个被访问的小区,他就会向右转。
醇>
他在桌子周围移动,尽可能多地探访细胞。你的任务是找出他停下来之前去过的牢房数量。
以下是Babai在9x9网格上的步骤示例。每个单元格的值表示步骤编号。
1 2 55 54 51 50 47 46 45
4 3 56 53 52 49 48 43 44
5 6 57 58 79 78 77 42 41
8 7 60 59 80 75 76 39 40
9 10 61 62 81 74 73 38 37
12 11 64 63 68 69 72 35 36
13 14 65 66 67 70 71 34 33
16 15 20 21 24 25 28 29 32
17 18 19 22 23 26 27 30 31
输入: 输入包含由线分隔的两个整数N和M. N和M介于1和100之间。
输出: 打印一个整数,这是测试用例的答案
示例输入#00:
3
3
示例输出#00:
9
示例输入#01:
7
4
示例输出#01:
18
实际查询:
现在我想到的一个答案就是在矩阵中标记受访节点,并遵循移动规则。然后在每次移动时递增计数器,并最后打印它。
但我发现另一个不使用此类代码复杂性的答案。我没有得到它。你能解释一下吗?
def move(n, m):
if m == 0 or n == 0:
return 0
elif m == 1 and n == 1:
return 1
elif (n % 2 == 1):
return 2 * n + move(m-2, n)
else:
return 2 * n
if __name__ == "__main__":
n = int(input("Enter number 1-100: "))
m = int(input("Enter another number 1-100: "))
print move(n, m)
答案 0 :(得分:2)
代码几乎是正确的。
它确定了4个案例。前两个是微不足道的:
if m == 0 or n == 0:
return 0
elif m == 1 and n == 1:
return 1
这些显然是正确的返回值,也可以作为第三种情况下使用的递归的终点。
elif (n % 2 == 1):
return 2 * n + move(m-2, n)
else:
return 2 * n
让我们先来看看最后一个案例。这是 n (行数)均匀时。正如你在这张照片中看到的那样,当 n 为4时,Babai卡在左下角:
不难看出在6行网格或任何偶数行网格上都会发生同样的情况。在所有这些情况下,网格的前两列与访问的单元格的集合相匹配。在每列中都有 n 单元格,解决方案确实是 2n 。
现在最棘手的情况是 n 是奇数。在这种情况下,Balai也将通过前两个列单元格,但最后一个访问的不是第一个,而是右边的那个:
同样,不难看出这不仅适用于图像中的5行,而且适用于任何奇数行的网格。
现在看看Babai即将进入的区域:它是一个大小为 m-2 列和 n 行的网格。这几乎就像是从头开始一个新的谜题,除了Babai从网格的左下角开始,而不是从左上角开始。现在想象我们顺时针转动整个网格:
现在,Babai即将进入的单元格正好,他将进入一个全新的网格。此外,当他进入那个“新”网格的角落单元时,他必须向右转......直到找到一个自由单元格,即第二列中的单元格。这是Babai在新拼图中所采用的相同路径。所以,......我们可以将步数的解决方案留给递归调用,我们需要交换列数和行数以模仿我们所做的90°转。
人们可能会怀疑这是否真的是同一件事,因为现在运动规则可能会有所不同。但事实并非如此。无论你面向哪个方向:向右转都是一样的动作。如果你的世界在你这样做的时候转过来并不重要:它会带你到同一个地方。所以,是的,我们可以在不影响运动规则的情况下转动网格。
因此我们在计算中有两个部分:
这可以写成代码的情况3中的递归公式:
return 2 * n + move(m-2, n)
递归的终点不够好。想象一下,我们输入 n = 1且 m = 2.这是一个非常简单的情况,很明显答案应该是2。
如果情况1和2不适用,并且 n 是奇数,则情况3:递归调用将是move(m-2,n)
,即move(-1,1)
,其中是...一个问题,因为在递归调用中前两个案例仍然不适用,所以这变成了一个无休止的递归调用链。
要修复它,请更改第二个条件并返回如下值(注意or
):
elif n == 1 or m == 1:
return n * m
这是正确的:如果网格有一行或一列,Babai将沿着该行走并访问其所有单元格(所以整个1维网格),其确实是 mn 单元格。 / p>
这种情况甚至可以吸收第一种情况,因为解决方案是0.所以纠正的代码是:
def move(n, m):
if m < 2 or n < 2:
return n * m
elif (n % 2 == 1):
return 2 * n + move(m-2, n)
else:
return 2 * n
if __name__ == "__main__":
n = int(input("Enter number 1-100: "))
m = int(input("Enter another number 1-100: "))
print move(n, m)
我们现在可以确定递归将停止:如果 n &lt; 2或 m &lt; 2,然后前两个案例中的一个将成立,并且不再进行进一步的递归调用。这意味着递归调用move(m-2, n)
现在是安全的。它会减少每次调用时的网格大小,但永远不会进入负数。
答案 1 :(得分:1)
答案在于您展示的9x9
矩阵。基本条件微不足道。
首先假设N是偶数。让我们说8.然后路径将终止于标记为16的小区,因为所有3个相邻小区都已经被访问过,并且没有其他路径可能。因此2*N
。
现在如果N是奇数(比如说9),那么在前两列之后,他将坐在标记为19并且朝上的单元格上。因此,现在问题从(N,M)
矩阵减少到(M-2,N)
矩阵
请注意,现在因为他面朝上,所以行和列互换。因此2*N + move(M-2, N)
。
答案 2 :(得分:0)
当N * M表包含至少5行和5列时,可以进一步优化问题,计算包含4行和4列的网格外层中的节点访问次数,然后进行递归调用N-4,M-4。相同的代码是
{{1}}