OJ: OpenJ_Bailian

题目 ID: 2083

标签: 分治

日期: 2026-01-21 21:02

这个问题是一个经典的**递归分形(Fractal)**构造问题。我们需要根据给定的递归规则,在一个二维网格中填充字符 ‘X’。

1. 核心规律分析

首先,我们要确定每一级分形的大小(边长):

  • n=1n=1 时,边长为 11 (303^0)。
  • n=2n=2 时,由 5 个 n=1n=1 的图形组成,排列成 3×33 \times 3 的布局,边长为 33 (313^1)。
  • n=3n=3 时,由 5 个 n=2n=2 的图形组成,排列成 3×33 \times 3 的布局,边长为 99 (323^2)。

通用结论: 度数为 nn 的盒分形,其边长为 S=3n1S = 3^{n-1}

2. 递归结构拆解

根据题干,度数为 nn 的图形 B(n)B(n) 是由 5 个度数为 n1n-1 的图形 B(n1)B(n-1) 组成的。如果我们把 B(n)B(n) 看作一个 3×33 \times 3 的九宫格,那么 B(n1)B(n-1) 分别占据了以下位置:

  1. 左上角
  2. 右上角
  3. 中间
  4. 左下角
  5. 右下角

3. 解题思路与算法

由于 nn 最大只有 7(边长 36=7293^6 = 729),我们可以开辟一个足够大的二维字符数组(例如 char canvas[730][730]),先初始化为空格,然后通过递归填充 ‘X’。

递归函数设计

我们可以定义一个函数 draw(int n, int r, int c),表示在起始坐标 (r,c)(r, c) 处绘制一个度数为 nn 的分形。

  • 递归边界:

    如果 n=1n=1,直接在 canvas[r][c] 处填入 ‘X’。

  • 递归步骤:

    1. 计算子图形(即 n1n-1 级)的边长:size=3n2size = 3^{n-2}
    2. 根据 3×33 \times 3 布局,递归调用 5 次:
      • 左上:draw(n-1, r, c)
      • 右上:draw(n-1, r, c + 2 * size)
      • 中间:draw(n-1, r + size, c + size)
      • 左下:draw(n-1, r + 2 * size, c)
      • 右下:draw(n-1, r + 2 * size, c + 2 * size)

4. 关键实现细节

  1. 预计算幂次方: 可以先计算好 303^0363^6 的值。
  2. 清空画布: 每次处理新的 nn 时,或者在程序开始前,将数组初始化为空格。
  3. 打印处理: 打印完每一行后,注意行末的空格。题目要求输出分形结构,通常需要打印到该行最后一个 ‘X’ 或者是完整的边长宽度。根据样例,直接打印整行(包括必要的空格)即可。
  4. 输出分隔符: 每一个测试用例结束后,打印一个只包含短横线 - 的行。

5. 伪代码示例

cpp
void draw(int n, int r, int c) {
    if (n == 1) {
        canvas[r][c] = 'X';
        return;
    }
    int size = pow(3, n - 2); // 计算下一级的边长
    draw(n - 1, r, c);                // 左上
    draw(n - 1, r, c + 2 * size);     // 右上
    draw(n - 1, r + size, c + size);  // 中间
    draw(n - 1, r + 2 * size, c);     // 左下
    draw(n - 1, r + 2 * size, c + 2 * size); // 右下
}

代码

cpp
#include <iostream>
#include <vector>
#include <cmath>
#include <cstring>

using namespace std;

// 全局字符数组,最大边长为 3^6 = 729
char canvas[731][731];

// 预计算 3 的幂次方,提高效率
int pow3[8];

/**
 * 递归绘制盒分形
 * @param n 当前分形的度数
 * @param r 起始行坐标
 * @param c 起始列坐标
 */
void draw(int n, int r, int c) {
    if (n == 1) {
        canvas[r][c] = 'X';
        return;
    }

    // 计算子分形(n-1度)的边长
    int subSize = pow3[n - 2];

    // 递归填充五个位置
    draw(n - 1, r, c);                         // 左上
    draw(n - 1, r, c + 2 * subSize);           // 右上
    draw(n - 1, r + subSize, c + subSize);     // 中间
    draw(n - 1, r + 2 * subSize, c);           // 左下
    draw(n - 1, r + 2 * subSize, c + 2 * subSize); // 右下
}

int main() {
    // 预处理 3 的幂
    pow3[0] = 1;
    for (int i = 1; i <= 7; ++i) pow3[i] = pow3[i - 1] * 3;

    int n;
    while (cin >> n && n != -1) {
        int size = pow3[n - 1];

        // 初始化画布为空格
        for (int i = 0; i < size; ++i) {
            for (int j = 0; j < size; ++j) {
                canvas[i][j] = ' ';
            }
            canvas[i][size] = '\0'; // 方便按行打印
        }

        // 开始递归绘制
        draw(n, 0, 0);

        // 输出结果
        for (int i = 0; i < size; ++i) {
            // 题目要求:去掉行末多余空格可能会导致不通过,
            // 建议直接输出整行。
            // 此时由于末尾有 \0,可以直接按字符串输出或遍历
            for (int j = 0; j < size; ++j) {
                cout << canvas[i][j];
            }
            cout << endl;
        }
        cout << "-" << endl;
    }
    return 0;
}