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;
}

 

posted @ 2024-06-04 11:43  五月江城  阅读(50)  评论(0编辑  收藏  举报