[ 数学知识 ]LCP 29. 乐团站位

272 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24 天,点击查看活动详情


题目链接:leetcode-cn.com/problems/SN…

题目描述

  • 某乐团的演出场地可视作 num * num 的二维矩阵 grid(左上角坐标为 [0,0]),每个位置站有一位成员。乐团共有 9 种乐器,乐器编号为 1~9,每位成员持有 1 个乐器。
  • 为保证声乐混合效果,成员站位规则为:自 grid 左上角开始顺时针螺旋形向内循环以 1,2,...,9 循环重复排列。例如当 num = 5 时,站位如图所示 image.png
  • 请返回位于场地坐标 [Xpos,Ypos] 的成员所持乐器编号。

示例

  • 示例1
输入:`num = 3, Xpos = 0, Ypos = 2`
输出:`3`
解释:

image.png

  • 示例2
输入:`num = 4, Xpos = 1, Ypos = 2`
输出:`5`
解释:

image.png

提示

  • 1 <= num <= 10^9
  • 0 <= 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
}

总结

  • 纯数学题,需要推导。想不到就是想不到