题解:洛谷 P5461 赦免战俘

【题目来源】

洛谷:P5461 赦免战俘 - 洛谷

【题目描述】

现有 \(2^n\times2^n(n\le10)\) 名作弊者站成一个正方形方阵等候 kkksc03 的发落。kkksc03 决定赦免一些作弊者。他将正方形矩阵均分为 \(4\) 个更小的正方形矩阵,每个更小的矩阵的边长是原矩阵的一半。其中左上角那一个矩阵的所有作弊者都将得到赦免,剩下 \(3\) 个小矩阵中,每一个矩阵继续分为 \(4\) 个更小的矩阵,然后通过同样的方式赦免作弊者……直到矩阵无法再分下去为止。所有没有被赦免的作弊者都将被处以棕名处罚。

给出 \(n\),请输出每名作弊者的命运,其中 \(0\) 代表被赦免,\(1\) 代表不被赦免。

【输入】

一个整数 \(n\)

【输出】

\(2^n\times2^n(n\le10)\)\(01\) 矩阵,代表每个人是否被赦免。数字之间有一个空格。

【输入样例】

3

【输出样例】

0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 1
0 0 0 0 0 1 0 1
0 0 0 0 1 1 1 1
0 0 0 1 0 0 0 1
0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1
1 1 1 1 1 1 1 1

【核心思想】

  1. 问题分析:给定 \(n\),生成一个 \(2^n \times 2^n\) 的 01 矩阵。规则是:将当前正方形矩阵均分为 4 个等大小的子矩阵,左上角子矩阵全部置为 0(赦免),其余三个子矩阵递归执行同样的操作,直到大小为 1 时停止(此时该位置置为 1?需根据输出理解:最终未被赦免的为 1)。本质是一个递归分形填充问题,类似“谢尔宾斯基三角形”的方形版本。

  2. 算法选择

    • 递归分治:函数 f(x, y, len) 处理左上角为 (x, y)、边长为 len 的正方形。若 len == 1,直接返回(或设置该点为 1);否则将左上角 len/2 × len/2 区域全部置为 0(赦免),然后对右上、左下、右下三个子区域递归调用(继续处理,这些区域最终会保留 1)。
    • 实现细节:代码中递归函数 fzdglen == 1 时直接返回,未显式设置 1,但最终未标记为 0 的位置默认输出 1(因为数组初始化为 0,而输出时 a[i][j]==1 输出 0,否则输出 1)。实际上,递归过程只负责将左上区域置为 1(标记为“填充/赦免”),其余保持 0,最后输出时反转。
  3. 关键步骤

    • 读入:读取 \(n\),计算边长 len = 2^n
    • 递归标记solve(1, 1, len),将左上角 len/2 × len/2 区域全部置为 1(表示被赦免,输出为 0),然后递归处理其他三个子区域。
    • 输出:按行输出矩阵,若 a[i][j] == 1 则输出 0,否则输出 1
  4. 时间/空间复杂度

    • 时间复杂度:\(O(4^n)\),即矩阵大小 \(2^n \times 2^n\),每个位置被访问常数次。
    • 空间复杂度:\(O(4^n)\),存储矩阵。
  5. 递归与分治

    • 分治结构:每次将问题规模缩小为原来的 1/2,处理三个子问题,符合分治思想。
    • 递归终止:当边长为 1 时,无法再分,递归结束。
    • 反向输出:代码中通过 a 数组标记被赦免区域,最后输出时反转,这是常见的技巧。
    • 适用场景:这类“递归填充矩阵”问题,是练习分治和递归的好例子,也常见于分形几何的模拟。

【算法标签】

普及 #递归

【代码详解】

#include <bits/stdc++.h>  // 包含标准库头文件(万能头文件)
using namespace std;      // 使用标准命名空间

int a[1100][1100];        // 定义二维数组:存储分形图案

/**
 * 递归生成分形图案
 * @param x 当前区域左上角的x坐标
 * @param y 当前区域左上角的y坐标
 * @param z 当前区域的边长
 * @return 无返回值
 */
int fzdg(int x, int y, int z)
{
    if (z == 1)           // 基本情况:最小单元直接返回
    {
        return 0;
    }

    // 填充当前区域左上角的1/4区域
    for (int i = x; i < x + z / 2; i++)
    {
        for (int j = y; j < y + z / 2; j++)
        {
            a[i][j] = 1;  // 标记为填充区域
        }
    }

    // 递归处理其他三个1/4区域
    fzdg(x + z / 2, y, z / 2);          // 右上区域
    fzdg(x, y + z / 2, z / 2);          // 左下区域
    fzdg(x + z / 2, y + z / 2, z / 2);  // 右下区域
}

int main()
{
    int n;                // 定义变量:分形迭代次数
    int fz;               // 定义变量:分形图案边长

    cin >> n;             // 输入迭代次数

    fz = pow(2, n);       // 计算分形图案边长(2的n次方)

    fzdg(1, 1, fz);       // 从(1,1)开始生成分形图案

    // 输出分形图案(0表示填充,1表示空白)
    for (int i = 1; i <= fz; i++)
    {
        for (int j = 1; j <= fz; j++)
        {
            if (a[i][j] == 1)
                cout << "0 ";  // 填充区域输出0
            else
                cout << "1 ";  // 空白区域输出1
        }
        cout << endl;          // 每行结束换行
    }

    return 0;                  // 程序正常结束
}

【运行结果】

3
0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 1
0 0 0 0 0 1 0 1
0 0 0 0 1 1 1 1
0 0 0 1 0 0 0 1
0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1
1 1 1 1 1 1 1 1
posted @ 2026-08-31 09:27  团爸讲算法  阅读(13)  评论(0)    收藏  举报