一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24 天,点击查看活动详情。
题目链接:leetcode-cn.com/problems/SN…
题目描述
- 某乐团的演出场地可视作 num * num 的二维矩阵 grid(左上角坐标为 [0,0]),每个位置站有一位成员。乐团共有 9 种乐器,乐器编号为 1~9,每位成员持有 1 个乐器。
- 为保证声乐混合效果,成员站位规则为:自 grid 左上角开始顺时针螺旋形向内循环以 1,2,...,9 循环重复排列。例如当 num = 5 时,站位如图所示
- 请返回位于场地坐标 [
Xpos,Ypos] 的成员所持乐器编号。
示例
- 示例1
输入:`num = 3, Xpos = 0, Ypos = 2`
输出:`3`
解释:
- 示例2
输入:`num = 4, Xpos = 1, Ypos = 2`
输出:`5`
解释:
提示
1 <= num <= 10^90 <= Xpos, Ypos < num
解题思路
- 根据题目分析
- 找到所在圈
- 将之前的圈所包含的数量加起来(注意溢出)
- 找到当前位置在该圈上的位置
- 求出最终数字
- 毫无疑问,就看成一圈一圈的正方形,然后定位点A(x,y)在第几个圈,判断代码如下:
直接取四个的最小值就是点A所处的圈
let whichRect = (Math.min(num - xPos - 1, xPos, yPos, num - yPos - 1)) - 然后计算,点A所处的圈的所有外圈的和,他们是等差数列(别想着用循环),直接用等差数列求和公式,这里很关键,单纯用公式的话,值会溢出,所以我们得对公式的乘积的每个元素取余,代码如下:
rectSum = (4 * whichRect % 9 * ((num - whichRect) % 9)) % 9 - 最后就是判断点A在正方形的上边还是右边还是下边,还是左边,分别添加即可。
反思
- 一开始思路是循环加和,但是
O(N)解法超时了。于是继续观察,发现每一圈都是等差数列,可以用等差数列求和来计算之前的圈所包含的数量,从而降低到O(1)。 - 直接计算会导致溢出,在计算
idx时需要包裹BigInt防止溢出。
AC代码
let rectSum = 0
let whichRect = (Math.min(num - xPos - 1, xPos, yPos, num - yPos - 1))
rectSum = (4 * whichRect % 9 * ((num - whichRect) % 9)) % 9
xPos = xPos - whichRect
yPos = yPos - whichRect
let tempSum = num - (whichRect) * 2
console.log(tempSum, whichRect, xPos, yPos, rectSum)
if (xPos === 0) {
return (rectSum + yPos + 1) % 9 === 0 ? 9 : (rectSum + yPos + 1) % 9
} else if (yPos === 0) {
return (rectSum + tempSum * 4 - 3 - xPos) % 9 === 0 ? 9 : (rectSum + tempSum * 4 - 3 - xPos) % 9
} else if (yPos + 1 === tempSum) {
return (rectSum + tempSum + xPos) % 9 === 0 ? 9 : (rectSum + tempSum + xPos) % 9
} else {
return (rectSum + tempSum * 3 - 3 - yPos + 1) % 9 === 0 ? 9 : (rectSum + tempSum * 3 - 3 - yPos + 1) % 9
}
总结
- 纯数学题,需要推导。想不到就是想不到