紫书刷题进行中,题解系列【GitHub|CSDN】
例题6-11 UVA297 Quadtrees(30行AC代码)
题目大意
一个四叉树有三类节点,即父结点(P),黑色叶子(f),白色叶子(e)
现给出两个四叉树,求合并后黑色像素个数。
思路分析
输入为四叉树的先序序列,因此先序递归建树很容易,但关键在于如何合并两棵四叉树,计算黑色个数。
- 尝试1:想同步层次/先序遍历两个四叉树,然后统计黑色个数,可惜两个树结构不完全一致,实现有些困难
- 尝试2:将四叉树转换为原图片的二维矩阵,然后用标记的方式来统计黑色个数,正解
尝试2的思路很有启发性,对于复杂的树/图结构,可以转换为规则的一维/二维数组,便于做各种计算
因此可以不用真正建立四叉树(类似UVA839,UVA699,隐式建树),在转换为原图二维矩阵时,可借用哈希的思想(空间换时间),遇见黑色就将整个正方形中的小格子全标记为黑色,因此可以直接统计黑色个数。
注意点
- 注意初始化数组为0
- 注意使用引用变量
- 可用技巧:四叉树转换为二维矩阵,隐式建树,空间换时间
AC代码(C++11,四叉树转二维矩阵,隐式建树,空间换时间)
#include<bits/stdc++.h>
using namespace std;
int T, cnt, img[1025][1025]={0}, idx;
// 根据s[idx]绘制左上角为(r,c),边长为w的正方形区域
void draw(string s, int& idx, int r, int c, int w) {
if (idx >= s.size()) return;
idx ++; // 先后移,注意引用
if (s[idx-1] == 'p') { // 父结点,绘制4个子方格
draw(s, idx, r, c+w/2, w/2); // 1
draw(s, idx, r, c, w/2); // 2
draw(s, idx, r+w/2, c, w/2); // 3
draw(s, idx, r+w/2, c+w/2, w/2); // 4
}
else if (s[idx-1] == 'f') { // 黑色才处理,白色默认0,不计数
for (int i = r; i < r+w; i ++) // 按最小方块标记上色
for (int j = c; j < c+w; j ++)
if (img[i][j] == 0) {cnt++; img[i][j] = 1;} // 统计黑色个数,记得加标记
}
}
int main() {
scanf("%d", &T);
string s1, s2;
while (T --) {
cin >>s1 >>s2;
cnt=0; fill(img[0], img[0]+1025*1025, 0); // 初始化
draw(s1, idx=0, 0, 0, 32); // 1024=32*32个格子
draw(s2, idx=0, 0, 0, 32); // 注意初始化idx=0
printf("There are %d black pixels.\n", cnt);
}
return 0;
}