Lccup 力扣杯春季编程大赛 2. 乐团站位 给定坐标和阶数 求螺旋数组中对应下标的值
题目
某乐团的演出场地可视作 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^9
- 0 <= Xpos, Ypos < num
思路1,超时
问题就一句话, 给定坐标和阶数 求螺旋数组中对应下标的值。
我最开始的是直接模拟法,代码如下,具体思路可看 旋转矩阵的构造和螺旋三角阵的构造,能过测试用例,but 超时,哈哈哈哈,所以这就不细讲了
class Solution {
public:
int orchestraLayout(int num, int xPos, int yPos) {
int row=num,column=num;
int max =1;
int l = 0, r=column-1, u = 0, d=row-1;
while (1) {
for (int i = l; (i <= r) && (max > 0); i++) {
if(u==xPos && i==yPos){
return max;
}
max++;
if(max>9){max=1;}
}
u++;
for (int i = u; (i <= d) && (max > 0); i++) {
if(i==xPos && r==yPos){
return max;
}
max++;
if(max>9){max=1;}
}
r--;
for (int i = r; (i >= l) && (max > 0); i--) {
if(d==xPos && i==yPos){
return max;
}
max++;
if(max>9){max=1;}
}
d--;
for (int i = d; (i >= u) && (max > 0); i--) {
if(i==xPos && l==yPos){
return max;
}
max++;
if(max>9){max=1;}
}
l++;
}
return max;
};
思路2 找规律
所以显然要找规律,那规律是啥呢?因为元素是一圈一圈填充进去的,每一圈的长也是和圈数有关系的,
- 所以我们可以先计算出来坐标在哪一圈,怎么计算呢,最简单的想法,xPos yPos里面谁比较小就是,但是我们再先想想,一圈要占两行两列,所以还要考虑 row-xPos 和 col-yPos,所以综上就是这四个谁小就是在那一圈,我们是方阵,所以 行和列最大下标都是
num-1
key = min(min(xPos,num-1-xPos),min(yPos,num-1-yPos))
- 有了圈数然后呢?有了圈数就可以算这圈之前填了多少数字,第 key 圈的边长是
size=num-2*key
,那么一圈的个数就是4*(size-1)=4*(num-2*key -1)= 4*(num-1) - 4*2*key
, - 其中
4*(num-1)
是固定的,4*2*key
是一个等差数列,那么算这圈之前填了多少数字就是一个等差数列求和的问题。 - 已知数列的通项公式为
f(key)=(4*(num-1) - 4*2*key)
key=[0, n] 其中 num 已知 ,求Sn(key-1)
- 带入求和公式
((首项+末项)*项数)/2
得到(4*(num-1)+(4*(num-1) - 8*key-8))*key/2
注意,项数是 [0,key-1] ,一共有 key 项,然后化简得4*(num-1)*key - 4*(key-1)*key
long long ret=0;
if (key>0){
ret+=(num-1)*key*4;
ret-=4*(key-1)*key;
}
- 计算完前面有多少个数字后,又该咋整呢,接着我们就看这个坐标究竟落在第 key 圈得上下左右那一条边上,然后就知道了具体数值了
class Solution
{
public:
int orchestraLayout(int num, int xPos, int yPos)
{
long long key=min(min(xPos,num-1-xPos),min(yPos,num-1-yPos));
long long ret=0;
long long size=num-key-key;
if (key>0){
ret+=(num-1)*key*4;
ret-=4*(key-1)*key;
}
//把key圈提出来当成一个新的方阵,方便计算 往左上方平移key个位置
xPos -= key;
yPos -= key;
if (xPos==0) //在上面一条边上,那就看看往右得y走了几个位置 +上去
ret+=yPos;
else if (yPos==size-1) //在右边一条边上,加上一条边的个数(size-1) 再看看往下走了几个位置 +上去
ret+=size-1+xPos;
else if (xPos==size-1) //在下面一条边上,加上两条边的个数 2 *(size-1) 再看看往右走了几个位置 +上去
ret+=(size-1)*2+(size-1-yPos);
else //在左面一条边上,加上三条边的个数 3 *(size-1) 再看看往上走了几个位置 +上去
ret+=(size-1)*3+(size-1-xPos);
return ret%9+1; //为什么+1是因为我们计算ret 的时候是从0开始的
}
};
如果好理解上面为什么要平移,也可以试着理解这个
class Solution {
public:
int orchestraLayout(int num, int xPos, int yPos) {
long long key=min(min(xPos,num-1-xPos),min(yPos,num-1-yPos));
long long ret=0;
if (key>0){
ret+=(num-1)*key*4;
ret-=4*(key-1)*key;
}
// xPos -= key;
// yPos -= key;
long long size=num-key-key;
if (xPos==key) //刚好在 第key圈的上面一条边
ret+=yPos-key; //第key圈,所以移动的时候要减掉之前的圈的个数
else if (yPos==num-1-key)
ret+=size-1+xPos-key;
else if (xPos==num-1-key)
ret+=(size-1)*2+(num-1-yPos-key);
else
ret+=(size-1)*3+(num-1-xPos-key);
return ret%9+1;
}
};