CSP历年复赛题-P2239 [NOIP2014 普及组] 螺旋矩阵
原题链接:https://www.luogu.com.cn/problem/P2239
题意解读:计算螺旋矩阵的第i行j列的数。
解题思路:
1、模拟法
按照螺旋矩阵的数字增长方向,依次枚举
定义四个方向(右、下、左、上)的坐标变换int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0}
初始方向为int dir = 0,按照初始方向进行坐标变换,一直到坐标越界或者前面的坐标已经走过,则变换方向dir = (dir + 1) % 4
每变换一次坐标,进行数字++,对矩阵填充数字,比较坐标是否是目标i,j,是则输出数字
前50%的矩阵大小n<=100,可以得到50分。
50分代码:
#include <bits/stdc++.h>
using namespace std;
int n, i, j;
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
int a[1005][1005];
int main()
{
cin >> n >> i >> j;
int dir = 0, ans = 1;
int x = 1, y = 1;
a[1][1] = 1;
while(!(x == i && y == j))
{
int nx = x + dx[dir], ny = y + dy[dir];
if(a[nx][ny] != 0 || nx < 1 || nx > n || ny < 1 || ny > n)
{
dir = (dir + 1) % 4;
continue;
}
ans++;
a[nx][ny] = ans;
x = nx, y = ny;
}
cout << ans;
return 0;
}
2、位置分析法
观察得知:矩阵最外圈的数字一共有n * 4 - 4个,往里一圈为(n - 2) * 4 - 4个,直到n-2=1则只有1个
对于给定的(i,j),如果能确定其所在圈层的边长w,那么更外层的数字可以通过边长枚举计算得出,然后对于边长w的圈层进行单独枚举即可
例如:
左图白色位置坐标(5, 3),计算所在圈层的边长方法为:
矩阵长度是6,分别计算(5, 3)在行坐标、列坐标上与中心线对称的行、列左标
对称的行坐标:6 - 5 + 1 = 2,对称的列坐标:6 - 3 + 1 = 4,所以(5, 3)所在的圈层边长应该是
max(abs(行左标-对称行坐标) + 1, abs(列坐标-对称列坐标) + 1) = max(4, 2) = 4
从n = 6开始枚举每一圈,如果n > 4,则数字直接累计当前圈层的数量,每一圈之后n -= 2,当n=4时,假设一共减了k次2,开始从(k+1,k+1)按螺旋形枚举每一个位置,直到(i,j)为止。
100分代码:
#include <bits/stdc++.h>
using namespace std;
int n, i, j, ans;
int main()
{
cin >> n >> i >> j;
int i2 = n - i + 1; //对称的行坐标
int j2 = n - j + 1; //对称的列坐标
int w = max(abs(i2 - i) + 1, abs(j2 - j) + 1); //(i,j)所在圈层的边长
int k;
for(k = 1; n != w; k++,n-=2)
{
ans += n * 4 - 4; //累计(i,j)以外圈层的数字个数
}
int x = k, y = k; //从(k,k)开始螺旋遍历边长为w的圈层
ans++; //(k,k)对应的数字
if(x == i && y == j)
{
cout << ans;
return 0;
}
//向右
for(int k = 1; k < w; k++)
{
ans++; y++;
if(x == i && y == j)
{
cout << ans;
return 0;
}
}
//向下
for(int k = 1; k < w; k++)
{
ans++; x++;
if(x == i && y == j)
{
cout << ans;
return 0;
}
}
//向左
for(int k = 1; k < w; k++)
{
ans++; y--;
if(x == i && y == j)
{
cout << ans;
return 0;
}
}
//向上
for(int k = 1; k < w - 1; k++)
{
ans++; x--;
if(x == i && y == j)
{
cout << ans;
return 0;
}
}
return 0;
}