题目:
如图所示,可以用四分图来表示一个黑白图像,方法是用根节点表示整幅图像,然后把行列个分成两等份,按图中的方式编号,从左到右对应4个子节点。如果某子节点对应的区域全黑或全白,则直接用一个黑节点或白节点表示;如既有黑又有白,则用一个灰节点表示,并且为这个区域递归建树。 给出两棵四分树的先序遍历,求二者合并(黑色部分合并)黑像素的个数(每幅图都是32X32的)。p表示灰节点,f表示黑节点,e表示白节点。
样例输入:
3
ppeeefpffeefe
pefepeefe
peeef
peefe
peeef
peepefefe
样例输出
There are 640 black pixels.
There are 512 black pixels.
There are 384 black pixels.
题目分析:
首先给出一颗四分树的前序遍历,我们可以通过前序遍历得出整个四分树,p代表灰色,如果输入了p,那么意味着它内部的4个格子,一定是有白有黑。这道题我们要算出所有黑色像素点,比如8*8的正方形内部有64个像素点。
解释一组输入样例:
ppeeefpffeefe
第一个p代表整个(第一层)正方形为灰色,第二个p,代表右上角的正方形为灰色,eeef(按题中所给的标号)代表右上角正方形内部四个格子颜色为白白白黑,接下来的p代表,左上角的格子为灰色,ffee代表左上角格子内部为黑黑白白,接下来的f代表,左下角的正方形为黑色,最后的e代表,右下角的正方形为白色。
解题思路:
读入前序遍历时,遇到p我们就要把大正方形分为4个小正方形,在小正方形中遇到p,还需要在分为小小的4个正方形。显然这是一种递归思想。我们在构建整个四分树时,传入参数r,c即正方形的左下角坐标,以及读入的操作,和正方形边长。如果操作为p,那我们递归调用构建方法,每次将边长除以2,以及根据当前左下角坐标,传入新的左下角坐标,如果操作为f,我们进行染色,将以(r,c)为左下角的二维数组染色,一直染到右上角(r+w,c+w),并且每次都将黑色像素点数字加一。如果是e我们不需要操作。
实现代码:
#include <bits/stdc++.h>
using namespace std;
int len=32;//边长
const int maxn=1024+10;//32*32=2^5*2^5=2^10=1024
char s[maxn];//树最多maxn个结点,也就是maxn个操作
int buf[32][32],cnt;//存放四分树
void draw(const char* s,int &p,int r,int c,int w)//传入数组时使用指针,传入参数时使用&
{
char ch=s[p++];//读入当前操作
if(ch=='p')
{
//从编号为1开始递归(r,c)为左下角
draw(s,p,r+w/2,c+w/2,w/2);//1
draw(s,p,r,c+w/2,w/2);//2
draw(s,p,r,c,w/2);//3
draw(s,p,r+w/2,c,w/2);//4
}else if(ch=='f')
{
for(int i=r;i<r+w;i++)
for(int j=c;j<c+w;j++)
{
if(buf[i][j]==0)//0表示白色
{
buf[i][j]=1;//1表示染黑
cnt++;
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(buf,0,sizeof(buf));
cnt=0;
for(int i=1; i<=2; i++)
{
scanf("%s",s);
int p=0;//统计读入到第几个字符
draw(s,p,0,0,len);//初始长度为32
}
printf("There are %d black pixels.\n",cnt);
}
return 0;
}
杂记:
我们并不用担心输入的是两颗树,在输入第二颗树时,会不会对结果造成影响,因为在染色时,读入第二颗树相当于对第一颗树进行进一步染色,原来是黑色还是黑色,原来是白色,读入的是黑色,新的黑色范围会包括原来的白色,会把白色染黑,并且新的操作是染白色也不会改变原来的黑色,因为我们直接跳过了白色的操作。