四分树

2:四分树

总时间限制: 
1000ms 
内存限制: 
65536kB
描述

一幅如图2(a)所示的二进制图片常常会用一个二进制矩阵来表示。所谓二进制矩阵是指矩阵中的每一个数不是0就是1。图2(b)展示图2(a)用二进制矩阵表示的情况。


图2:(a)二进制图片(b)图片的矩阵表示(c)四分树划分(d)四分树表示

 

为了保存图2(b)这样的矩阵,经常使用四分树来完成。对于一个N * N的矩阵,N <= 512且N = 2^i(i为正整数),如果这个矩阵中的数不全一样,那么我们会把这个矩阵分成4个N/2 * N/2的矩阵,如图2(c)所示。之后,我们再对这4个N/2 * N/2的矩阵划分,同样地,如果里面的数不全一样则划分成N/4 * N/4的矩阵。图2(c)里面右边的两个N/2 * N/2的矩阵就被这样再度划分了。如此可以持续进行划分,直到里面的数全一样。图2(c)展示了完全划分完毕的样子。

我们一般都将二进制图片存成图2(d)这样的四分树的形式,这棵树是通过图2(c)里面的划分得到的。图2(d)里面的每一个结点都代表图2(c)里面的矩阵,而树的根结点代表整个大的矩阵。如果树中一个结点的值为1,则代表这个结点对应的矩阵需要划分成4个小矩阵。否则,这个结点将包含两个数。第一个数为0,表示不用再划分,第二个数为0或者1,表示整个矩阵都是这个值。整棵树可以用它的宽度优先遍历得到的结果来表示,如图2(d)中的树可以表示成(1)(0,0)(1)(0,1)(1)(0,0)(0,1)(1)(0,0)(0,0)(0,0)(0,1)(0,1)(0,0)(0,1)(0,0)(0,1)。删掉括号和逗号,我们可以得到一个更简短的纯二进制编码100101100011000000010100010001来编码这张图片,它的16进制形式为258C0511。

现在请你编写一个程序,求出给定图片的16进制形式的编码。

 


输入
第1行包含一个数k,1 <= k <= 100,表示数据的组数。
对于每一组数据,第1行包含一个数N表示图片的大小为N * N,其中N <= 512且N = 2^i(i为正整数)。
接下来跟着一个N * N的矩阵代表一张二进制图片。每两个0和1之间至少有一个空格。
输出
每张图片通过四分树得到的16进制编码。
样例输入
320 00 040 0 1 10 0 1 11 1 0 01 1 0 080 0 0 0 0 0 1 10 0 0 0 0 0 1 10 0 0 0 0 1 0 00 0 0 0 0 1 0 01 1 1 1 0 0 0 01 1 1 1 0 0 0 01 1 1 1 1 1 1 11 1 1 1 1 1 1 1
样例输出
0114258C0511
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<map>
using namespace std;
char Map[513][513];
struct Node
{
	string s;
	Node*Child[4];
	Node()
	{
		s="";
		memset(Child,0,sizeof(Child));
	}
};
Node*root;
int Check(int x,int y,int size)
{
	int num0=0,num1=0;
	for(int i=x;i<x+size;++i)
		for(int j=y;j<y+size;++j) 
		{
			if(Map[i][j]=='0')num0++;
			else num1++;
		}
	if(num0==0)return 1;
	if(num1==0)return 0;
	return -1;
}
Node*dfs(int x,int y,int size)
{
	Node* p=new Node();
	if(size==1)
	{	
		p->s="0";
		p->s+=Map[x][y];
		return p;
	}
	int tag=Check(x,y,size);
	if(tag==0)
	{
		p->s="00";
		return p;
	}
	if(tag==1)
	{
		p->s="01";
		return p;
	}
	p->s="1";
	int len=size>>1;
	p->Child[0]=dfs(x,y,len);
	p->Child[1]=dfs(x,y+len,len);
	p->Child[2]=dfs(x+len,y,len);
	p->Child[3]=dfs(x+len,y+len,len);
	return p;
}
void Print(int x)
{
	if(x<=9)cout<<x;
	else if(x==10)cout<<"A";
	else if(x==11)cout<<"B";
	else if(x==12)cout<<"C";
	else if(x==13)cout<<"D";
	else if(x==14)cout<<"E";
	else if(x==15)cout<<"F";
}
void bfs(Node*root)
{
	queue<Node*>q;
	q.push(root);
	string s="";
	while(!q.empty())
	{
		Node*tmp=q.front();
		q.pop();
		s+=tmp->s;
		for(int i=0;i<4;++i)
		{
			if(tmp->Child[i]!=NULL)
			{
				q.push(tmp->Child[i]);
			}
		}
	}
	int len=s.length();
	int pre=len%4;
	int sum=0,x=1;
	if(pre>0)
	{
		for(int i=pre-1;i>=0;--i)
		{
			sum+=x*(s[i]-'0');
			x<<=1;
		}
		Print(sum);
	}
	for(int i=pre;i<len;i+=4)
	{
		sum=0;x=1;
		for(int j=i+3;j>=i;--j)
		{
			sum+=x*(s[j]-'0');
			x<<=1; 
		}
		Print(sum);
	}
	cout<<endl;
}
int main()
{
	int Test;
	cin>>Test;
	while(Test--) 
	{
		int n;
		cin>>n;
		for(int i=0;i<n;++i)
		{
			for(int j=0;j<n;++j)
			{
				cin>>Map[i][j];
			}
		}
		root=dfs(0,0,n);
		bfs(root);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值