【题目链接】
ybt 1967:【14NOIP普及组】螺旋矩阵
洛谷 P2239 [NOIP2014 普及组] 螺旋矩阵
类似考题:
洛谷 P1014 [NOIP1999 普及组] Cantor 表
【题目考点】
1.二维数组
【解题思路】
记输入的目标位置为(ai, aj)
如果暴力移动焦点,在二位数组中蛇形填写数字,因为数组边长达到30000,数组元素个数为
30000
∗
30000
=
9
∗
1
0
8
30000*30000=9*10^8
30000∗30000=9∗108,默认计算机1s可以进行小于
1
0
8
10^8
108次运算,这样做一定会超时。
每圈的元素个数是容易求的,如果每次移动一圈,到(ai,aj)所在的圈时再一个一个格子移动,这样移动次数就会大大减少。
第1圈边长为n,元素个数为4*(n-1)
第2圈边长为n-2,元素个数为4*(n-2-1)
…
第i圈边长为n-2*(i-1),元素个数为4*(n-2*(i-1)-1)
每圈边长比上一圈少2,元素个数为4*(边长-1)
第i圈:上面一行的行坐标为:i,下面一行行坐标为:n-i+1,左侧一列列坐标为:i,右侧一列列坐标为n-i+1。
如果目标位置:(ai, aj)的行坐标ai为i或n-i+1,或列坐标aj为i或n-i+1,说明(ai,aj)在第i圈。然后可以有两种方法确定(ai, aj)位置的值。
解法1:移动焦点
第i圈边长为l=n-2*(i-1),将一圈分为等长的四部分,每部分长度为:l-1。焦点从左上角出发,向右移动l-1次后,焦点指向右上角;再向下移动l-1次后,焦点指向右下角;再向左移动l-1次,焦点指向左下角;再向上移动l-1次,完成对第i圈的遍历。
在这一过程中,记录到每个位置时数组元素值。如果遍历到(ai, aj),输出该值。
解法2:坐标计算
记第i圈左上角位置(i,i)的值为st,边长为l。那么右上角(i,n-i+1)的值为:st+l-1,右下角(n-i+1,n-i+1)的值:st+(l-1)*2,左下角(n-i+1,i)的值:st+(l-1)*3。
- 如果目标位置在第i行,需要从左上角(i,i)移动到(ai,aj)位置,移动aj-i次,值需要加aj-i,那么(ai,aj)位置的值为st+aj-i
- 如果目标位置在第n-i+1列,需要从右上角(i,n-i+1)移动到(ai,aj)位置,移动ai-i次,那么(ai,aj)位置的值为st+(l-1)+ai-i
- 如果目标位置在第n-i+1行,需要从右下角(n-i+1,n-i+1)移动到(ai,aj)位置,移动n-i+1-aj次,那么(ai,aj)位置的值为st+2*(l-1)+(n+1-i)-aj
- 如果目标位置在第i列,需要从左下角(n-i+1,i)移动到(ai,aj)位置,移动n-i+1-ai次,那么(ai,aj)位置的值为st+3*(l-1)+(n+1-i)-ai
【题解代码】
解法1:移动焦点
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n, ai, aj, i, st, l;//求(ai,aj)的数 st:该圈起始数字
int dir[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};//右下左上
cin >> n >> ai >> aj;
st = 1, l = n;//st:起始数字, l:该圈边长
for(i = 1; i <= n/2; ++i)
{//看ai,aj是否在第i圈
if(ai == i || ai == n+1-i || aj == i || aj == n+1-i)
break;
st += 4*l-4;
l -= 2;
}
//看ai,aj位置是第i圈上第几个格子,求出目标位置的值
int fi = i, fj = i, num = st;//fi,fj:焦点 num:该位置的值
for(int d = 0; d < 4; ++d)//方向 0:右 1:下 2:左 3:上
{
int k = 1;
do//用do...while是为了当l==1时,也会判断输出解。解决找边长为奇数的二维数组中心位置的问题。
{
if(fi == ai && fj == aj)//找到目标位置
{
cout << num;
return 0;
}
fi += dir[d][0], fj += dir[d][1];//按照d方向移动到下一个位置
num++;
k++;
}while(k <= l-1);//每一条边遍历l-1次
}
return 0;
}
解法2:坐标计算
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n, ai, aj, i, st, l;//求(ai,aj)的数 st:该圈起始数字
cin >> n >> ai >> aj;
st = 1, l = n;//st:起始数字, l:该圈边长
for(i = 1; i <= n/2; ++i)
{//看ai,aj是否在第i层外圈
if(ai == i || ai == n+1-i || aj == i || aj == n+1-i)
break;
st += 4*l-4;
l -= 2;
}
if(ai == i)//在第i行
cout << st+aj-i;//移动aj-i次
else if(aj == n+1-i)//在第n-i+1列
cout << st+l-1+ai-i;//移动ai-i次
else if(ai == n+1-i)//在第n-i+1行
cout << st+2*(l-1)+(n+1-i)-aj;//移动(n+1-i)-aj次
else if(aj == i)//在第i列
cout << st+3*(l-1)+(n+1-i)-ai;//移动(n+1-i)-ai次
return 0;
}
解法3:递归
#include<bits/stdc++.h>
using namespace std;
int getNum(int l, int i, int j)//左上角(1,1)位置的值为1,边长为l的矩阵中(i,j)的值
{
if(i == 1)//如果在上边
return j;
else if(j == l)//如果在右边
return l-1+i;
else if(i == l)//如果在下边
return 2*(l-1)+l-j+1;
else if(j == 1)//如果在左边
return 3*(l-1)+l-i+1;
else//如果在内部 i,j位置的值为这一圈元素的个数(4*(l-1))加上以(2,2)位置为左上角,以l-2长度为边的矩阵中,(i-1,j-1)位置的值。
return 4*(l-1)+getNum(l-2, i-1, j-1);//以(2,2)位置为左上角时, 原(2,2)变为(1,1),原(i,j)变为(i-1,j-1)
}
int main()
{
int n, ai, aj;//求(ai,aj)的数 st:该圈起始数字
cin >> n >> ai >> aj;
cout << getNum(n, ai, aj);
return 0;
}