2 条题解

  • 0
    @ 2025-12-15 9:28:47

    这是一个用于生成 B3940 [GESP样题 四级] 填幻方 测试数据的完整 C++ 代码。

    由于题目中 NN 的范围是 3N213 \le N \le 21NN 为奇数,所有可能的输入值恰好有 10 个(3, 5, 7, ..., 21)。因此,这个生成器会将这 10 个可能的 NN 值分别作为 1 到 10 号测试点,从而实现对所有情况的完全覆盖

    数据生成器代码 (generator.cpp)

    您可以将以下代码保存为 generator.cpp,编译并运行。它将在当前目录下生成 1.in/1.out10.in/10.out

    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <string>
    #include <iomanip>
    
    using namespace std;
    
    // ==========================================
    // 1. 标准解法函数 (用于生成 .out 文件)
    // ==========================================
    void solve(int N, ofstream &fout) {
        // 使用 vector 动态调整大小,初始化为 0
        vector<vector<int>> magic(N, vector<int>(N, 0));
    
        // 1. 在第一行的正中央填上 1
        int row = 0;
        int col = N / 2;
        magic[row][col] = 1;
    
        // 循环填入 2 到 N*N
        for (int k = 2; k <= N * N; ++k) {
            // 计算向右上移动后的坐标 (上一行,下一列)
            // (row - 1 + N) % N 处理向上越界
            // (col + 1) % N 处理向右越界
            int next_row = (row - 1 + N) % N;
            int next_col = (col + 1) % N;
    
            // 检查移动后的位置是否为空
            if (magic[next_row][next_col] == 0) {
                // 如果为空,确认移动到该位置
                row = next_row;
                col = next_col;
            } else {
                // 如果不为空,则从"上次填数字的位置"向下移动一格
                row = (row + 1) % N;
                // col 保持不变
            }
    
            // 填入数字
            magic[row][col] = k;
        }
    
        // 输出幻方到文件
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                fout << magic[i][j] << (j == N - 1 ? "" : " ");
            }
            fout << "\n";
        }
    }
    
    // ==========================================
    // 2. 测试点生成逻辑
    // ==========================================
    void generate_test_case(int id) {
        // 题目要求 N 为奇数,且 3 <= N <= 21
        // 可能的 N 值为: 3, 5, 7, 9, 11, 13, 15, 17, 19, 21
        // 恰好 10 个,直接映射 id 1~10 到这些值
        int N = 2 * id + 1;
    
        // --- 写入 .in 文件 ---
        string in_filename = to_string(id) + ".in";
        ofstream fin(in_filename);
        fin << N << "\n";
        fin.close();
    
        // --- 计算答案并写入 .out 文件 ---
        string out_filename = to_string(id) + ".out";
        ofstream fout(out_filename);
        solve(N, fout);
        fout.close();
    
        cout << "Generated Case " << id << ": N=" << N << endl;
    }
    
    int main() {
        // 生成 1 到 10 号测试点
        for (int i = 1; i <= 10; ++i) {
            generate_test_case(i);
        }
        cout << "All test cases generated successfully!" << endl;
        return 0;
    }
    

    测试点覆盖说明

    本生成器采用了全覆盖策略,因为题目数据范围较小且离散:

    1. Case 1: N=3N=3 (题目样例,最小边界)
    2. Case 2: N=5N=5
    3. Case 3: N=7N=7
    4. Case 4: N=9N=9
    5. Case 5: N=11N=11 (中等规模)
    6. Case 6: N=13N=13
    7. Case 7: N=15N=15
    8. Case 8: N=17N=17
    9. Case 9: N=19N=19
    10. Case 10: N=21N=21 (最大边界)

    每个测试点都严格遵循题目描述的“罗伯法”(Loubère method)生成标准答案。

    使用方法

    1. 保存:将代码保存为 generator.cpp
    2. 编译:使用 C++ 编译器编译(如 g++ generator.cpp -o generator)。
    3. 运行:运行生成的可执行文件(如 ./generatorgenerator.exe)。
    4. 结果:当前目录下会生成 10 组 .in.out 文件,可直接上传至 OJ 系统。
    • 0
      @ 2025-12-15 9:26:55

      这是一个经典的构造奇数阶幻方的问题,所描述的方法被称为“罗伯法”(Loubère method)。

      题目分析

      1. 输入:一个正奇数 NN (3N213 \le N \le 21)。
      2. 目标:构造并输出一个 N×NN \times N 的幻方。
      3. 规则
        • 数字 1 放在第一行的正中间。
        • 下一个数字填在当前位置的右上方(行号减1,列号加1)。注意处理边界的“回绕”情况:
          • 如果行号减1后小于0,则变到最后一行。
          • 如果列号加1后超过最大列索引,则变到第一列。
        • 冲突处理:如果右上方的位置已经填了数字,则改为填在当前位置的正下方(行号加1)。

      算法实现

      我们可以使用一个二维数组 a[N][N] 来存储幻方。 设当前数字的位置为 (row,col)(row, col)。 对于下一个数字 kk (2kN×N2 \le k \le N \times N):

      1. 计算预想位置:next_row = (row - 1 + N) % Nnext_col = (col + 1) % N
        • 这里 (row - 1 + N) % N 可以巧妙地处理向上越界回到最后一行的情况。
        • (col + 1) % N 可以处理向右越界回到第一列的情况。
      2. 检查 a[next_row][next_col] 是否为 0(即是否为空)。
        • 若为空,则 kk 填入该位置,更新 row = next_row, col = next_col
        • 若不为空,则 kk 填入当前位置的正下方:row = (row + 1) % Ncol 不变。

      C++ 代码

      #include <iostream>
      #include <vector>
      
      using namespace std;
      
      int main() {
          int N;
          if (!(cin >> N)) return 0;
      
          // 定义一个二维数组,初始化为0
          // N 最大为 21,开 25x25 足够
          int magic[25][25] = {0};
      
          // 1. 在第一行的正中央填上 1
          // 行下标从 0 开始,列下标从 0 到 N-1
          int row = 0;
          int col = N / 2;
          magic[row][col] = 1;
      
          // 循环填入 2 到 N*N
          for (int k = 2; k <= N * N; ++k) {
              // 计算向右上移动后的坐标
              // (row - 1 + N) % N 实现向上移动并在越界时回到最后一行
              // (col + 1) % N 实现向右移动并在越界时回到第一列
              int next_row = (row - 1 + N) % N;
              int next_col = (col + 1) % N;
      
              // 检查移动后的位置是否为空
              if (magic[next_row][next_col] == 0) {
                  // 如果为空,确认移动到该位置
                  row = next_row;
                  col = next_col;
              } else {
                  // 如果不为空,则从"上次填数字的位置"向下移动一格
                  row = (row + 1) % N;
                  // col 保持不变
              }
      
              // 填入数字
              magic[row][col] = k;
          }
      
          // 输出幻方
          for (int i = 0; i < N; ++i) {
              for (int j = 0; j < N; ++j) {
                  cout << magic[i][j] << (j == N - 1 ? "" : " ");
              }
              cout << endl;
          }
      
          return 0;
      }
      
      • 1

      信息

      ID
      14673
      时间
      1000ms
      内存
      32MiB
      难度
      3
      标签
      递交数
      2
      已通过
      1
      上传者