1. 题目背景与问题解析
"P5461 赦免战俘"这道题目源自洛谷在线评测系统,属于典型的递归与分治算法练习题。题目描述了一个有趣的场景:在2^n × 2^n的正方形战俘营中,需要按照特定规则赦免部分战俘,最终输出赦免后的战俘分布情况。
这道题的核心在于理解赦免规则:
- 初始状态:所有战俘标记为1(表示未被赦免)
- 每次操作:将当前正方形区域划分为4个更小的正方形,赦免(置0)左上角的那个小正方形
- 递归执行:对剩下的3个小正方形重复上述过程,直到无法再分割
2. 递归分治算法设计
2.1 基本思路
解决这类问题的标准方法是递归分治:
- 定义递归函数:void pardon(int x, int y, int size)
- (x,y)表示当前区域的左上角坐标
- size表示当前区域的边长
- 递归终止条件:当size==1时直接返回
- 递归过程:
- 赦免左上角区域(置0)
- 对剩余三个区域继续递归
2.2 代码实现框架
#include <iostream> #include <vector> using namespace std; vector<vector<int>> camp; void pardon(int x, int y, int size) { if(size == 1) return; // 赦免左上角区域 for(int i=x; i<x+size/2; i++) { for(int j=y; j<y+size/2; j++) { camp[i][j] = 0; } } // 处理剩余三个区域 pardon(x, y+size/2, size/2); // 右上 pardon(x+size/2, y, size/2); // 左下 pardon(x+size/2, y+size/2, size/2); // 右下 } int main() { int n; cin >> n; int size = 1 << n; camp.resize(size, vector<int>(size, 1)); pardon(0, 0, size); // 输出结果 for(int i=0; i<size; i++) { for(int j=0; j<size; j++) { cout << camp[i][j] << " "; } cout << endl; } return 0; }3. 算法优化与空间复杂度分析
3.1 空间优化技巧
原始解法使用了二维数组存储整个战俘营,空间复杂度为O((2^n)^2)。实际上可以利用分治的特性进行优化:
- 位运算优化:可以用bitset代替二维vector
- 按行处理:可以逐行计算,减少内存使用
- 数学规律:观察输出模式,发现可以找到每个位置(i,j)是否被赦免的数学表达式
3.2 数学规律解法
经过观察可以发现:
- 位置(i,j)被赦免当且仅当i和j的二进制表示在某一位上同时为0
- 即(i & j) != 0时保留1,否则置0
优化后的代码:
#include <iostream> using namespace std; int main() { int n; cin >> n; int size = 1 << n; for(int i=0; i<size; i++) { for(int j=0; j<size; j++) { cout << ((i & j) ? 0 : 1) << " "; } cout << endl; } return 0; }4. 边界条件与测试用例
4.1 常见边界情况
- n=0时的处理(理论上size=1)
- n=1时的最小非平凡情况
- n=10时的最大规模测试(1024×1024矩阵)
4.2 测试用例示例
输入1:
3输出1:
1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 0 0 1 1 0 0 1 0 0 0 1 0 0 0 1 1 1 1 0 0 0 0 1 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0输入2:
2输出2:
1 1 1 1 1 0 1 0 1 1 0 0 1 0 0 05. 算法复杂度分析
5.1 时间复杂度
- 递归解法:O((2^n)^2),因为每个位置最多被访问一次
- 数学解法:O((2^n)^2),但常数因子更小
5.2 空间复杂度
- 递归解法:O((2^n)^2)用于存储矩阵,加上递归栈O(n)
- 数学解法:O(1)额外空间(不存储整个矩阵)
6. 实际应用与扩展
6.1 类似问题
- 分形生成(如谢尔宾斯基地毯)
- 四叉树构建
- 图像处理中的区域分割
6.2 扩展思考
- 如果赦免规则改为赦免其他位置的子区域会怎样?
- 如何优化输出过程以避免超时(特别是n较大时)?
- 如果要求统计被赦免和未被赦免的战俘数量,如何高效计算?
在实际编程竞赛中,这类题目考察选手对递归和分治思想的理解,以及对问题规律的观察能力。建议初学者从递归解法入手,理解透彻后再尝试寻找数学规律进行优化。