题解:洛谷 AT_abc436_b Magic Square

【题目来源】

洛谷:AT_abc436_b [ABC436B] Magic Square - 洛谷

【题目描述】

给定一个奇数 \(N\),且 \(N\geq 3\)

有一个 \(N\)\(N\) 列的网格,初始时所有格子都是空的。现在,你将按照如下步骤在每个格子里写入整数。令 \((i,j)\) 表示第 \(i+1\) 行(自顶向下)第 \(j+1\) 列(自左向右)的格子(\(0\leq i<N, 0\leq j<N\))。

  1. 在格子 \((0,\frac{N-1}{2})\) 中写入 \(1\)
  2. 重复以下操作 \(N^2-1\) 次:
    • \((r,c)\) 是上一次写入整数的格子,\(k\) 是上一次写入的整数。如果格子 \(((r-1)\bmod N, (c+1)\bmod N)\) 为空,则在该格子写入 \(k+1\);否则,在格子 \(((r+1)\bmod N, c)\) 中写入 \(k+1\)。其中 \(x\bmod N\) 表示 \(x\) 除以 \(N\) 的余数。

请找出这个过程中每个格子里最终写入的整数。可以证明每个格子恰好只会被写入一个整数。

【输入】

输入从标准输入按以下格式给出:

\(N\)

【输出】

\(a_{i,j}\) 表示第 \((i,j)\) 个格子里写入的整数,输出格式如下:

\(a_{0,0}\ a_{0,1}\ \dots\ a_{0,N-1}\)
\(a_{1,0}\ a_{1,1}\ \dots\ a_{1,N-1}\)
\(\vdots\)
\(a_{N-1,0}\ a_{N-1,1}\ \dots\ a_{N-1,N-1}\)

【输入样例】

3

【输出样例】

8 1 6
3 5 7
4 9 2

【核心思想】

  1. 问题分析:给定奇数 \(N\),需要按照特定规则构造 \(N \times N\) 的幻方(Magic Square)。规则为:从第一行中间开始填 \(1\),之后每次尝试往当前格子的右上角填下一个数;若右上角已被占用或越界(通过取模循环处理),则改为填在当前格子的正下方。这是一个直接模拟/规则构造问题,关键在于正确实现坐标变换和边界循环。

  2. 算法选择

    • 直接模拟:严格按照题目给定的构造规则,逐步填入 \(1\)\(N^2\) 的整数
    • 循环边界处理:使用 \((x + N) \% N\) 实现越界时的循环回绕(如第 \(-1\) 行变为第 \(N-1\) 行)
  3. 关键步骤

    • 初始化:创建 \(N \times N\) 的空矩阵 \(a\),初始值全为 \(0\)
    • 放置 \(1\)\(a[0][(N-1)/2] = 1\),当前位置 \((i, j) = (0, (N-1)/2)\)
    • 循环放置\(k\)\(1\)\(N^2 - 1\)):
      • 计算候选位置(右上角):\(next\_i = (i - 1 + N) \% N\)\(next\_j = (j + 1) \% N\)
      • 判断分支
        • \(a[next\_i][next\_j] = 0\)(空):填入 \(k+1\),更新 \((i, j) = (next\_i, next\_j)\)
        • 若已被占用:填入 \(a[(i+1) \% N][j] = k+1\),更新 \(i = (i+1) \% N\)(正下方)
      • \(k \mathrel{+}= 1\)
    • 输出矩阵:按行优先输出 \(a\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^2)\),恰好填入 \(N^2\) 个数,每个数 \(O(1)\) 计算位置
    • 空间复杂度:\(O(N^2)\),存储 \(N \times N\) 的幻方矩阵
  5. 规则模拟的核心思想

    • Siamese Method(暹罗法):该构造规则是经典的奇数阶幻方生成算法,保证每行、每列、对角线之和相等
    • 循环坐标系:通过取模运算将二维网格视为环面(torus),消除边界特殊性,统一处理所有位置
    • 贪心占位:始终优先尝试右上角,仅在冲突时回退到正下方,确保 \(N^2\) 个位置恰好被填满且不重复
    • 适用于"按固定规则逐步构造矩阵/序列"类问题,核心是准确翻译题目规则为代码逻辑

【算法标签】

普及- #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 105;  // 最大矩阵大小
int n;              // 幻方大小(奇数)
int a[N][N];        // 幻方矩阵

int main()
{
    // 输入幻方大小n(应为奇数)
    cin >> n;

    // 1. 初始化:将1放在第一行的中间列
    a[0][(n - 1) / 2] = 1;
    int k = 1;  // 当前已放置的数字

    // 当前位置:i行,j列
    int i = 0, j = (n - 1) / 2;

    // 2. 放置剩余的n*n-1个数字
    while (k <= n * n - 1)
    {
        // 计算右上角的位置(循环处理)
        int next_i = (i - 1 + n) % n;  // 上一行
        int next_j = (j + 1) % n;      // 右一列

        // 如果右上角位置为空
        if (a[next_i][next_j] == 0)
        {
            // 将下一个数字放在右上角
            a[next_i][next_j] = k + 1;
            // 更新当前位置
            i = next_i;
            j = next_j;
        }
        else
        {
            // 如果右上角被占用,放在正下方
            a[(i + 1) % n][j] = k + 1;
            // 更新当前位置
            i = (i + 1) % n;  // 下一行
            j = j;            // 同一列
        }

        k++;  // 已放置数字数量加1

        // 调试输出
        // cout << "i j " << i << " " << j << endl;
    }

    // 3. 输出幻方
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            cout << a[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}

【运行结果】

3
8 1 6
3 5 7
4 9 2
posted @ 2026-08-27 11:04  团爸讲算法  阅读(7)  评论(0)    收藏  举报