ZOJ 1788 Quad Trees (四分树经典)

Quad Trees

题目传送门~

Time Limit: 2000 msMemory Limit: 65536 KB

A binary image, such as the one shown in Figure 2(a), is usually represented as an array of binary entries, i.e., each entry of the array has value 0 or 1. Figure 2(b) shows the array that represents the binary image in Figure 2(a). To store the binary image of Figure 2(b), the so-called quad tree partition is usually used. For an N N array, N <= 512 and N = 2^i for some positive integer i, if the entries do not have the same value, then it is partitioned into four N/2 N/2 arrays, as shown in Figure 2(c). If an N/2N/2 array does not have the same binary value, such as the upper right and lower right N/2N/2 arrays in Figure 2(c), then we can divide it into four N/4N/4 arrays again. These N/4N/4 arrays in turn can also, if needed, be divided into four N/8 N/8 arrays, etc.. The quad tree partition is completed when the whole array is partitioned into arrays of various size in which each array contains only one binary value. Figure 2(c) contains the arrays after the quad tree partition is completed.

 
Figure 2: A binary image (a), its array representation (b), its quad tree partition (c), and its quad tree representation (d).

Instead of storing the binary image of Figure 2(a), we only need to store the quad tree in the form as Figure 2(d) which is encoded from Figure 2(c). In Figure 2(d), each node represents an array of Figure 2(c) in which the root node represents the original array. If the value of a node in the tree is 1, then it means that its corresponding array needs to be decomposed into four smaller arrays. Otherwise, a node will have a pair of values and the first one is 0. It means that its corresponding array is not necessary to decompose any more. In this case, the second value is 0 (respectively, 1) to indicate that all the entries in the array are 0 (respectively, 1). Thus, we only need to store the tree of Figure 2(d) to replace storing the binary image of Figure 2(a). The way to store the tree of Figure 2(d) can be represented by the following code:

(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).

This code is just to list the values of the nodes from the root to leaves and from left to right in each level. Deleting the parentheses and commas, we can obtain a binary number 100101100011000000010100010001 which is equal to 258C0511 in hexadecimal. You are asked to design a program for finding the resulting hexadecimal value for each given image.


Input

There is an integer number k, 1 <= k <= 100, in the first line to indicate the number of test cases. In each test case, the first line is also a positive integer N indicating that the binary image is an N N array, where N <= 512 and N = 2^i for some positive integer i. Then, an N N binary array is followed in which at least one blank is between any two elements.


Output

The bit stream (in hexadecimal) used to code each input array.


Sample Input

3
2
0 0
0 0
4
0 0 1 1
0 0 1 1
1 1 0 0
1 1 0 0
8
0 0 0 0 0 0 1 1
0 0 0 0 0 0 1 1
0 0 0 0 0 1 0 0
0 0 0 0 0 1 0 0
1 1 1 1 0 0 0 0
1 1 1 1 0 0 0 0
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1


Sample Output

0
114
258C0511


题后两句话:

①啊啊啊啊啊这个四分树是真的冷门而且不简单,理解还挺好理解的,就是要考虑的细节超级多哭辽。

②顺一波题意康康:输入一个整数num:1 ~100,表示测试的组数,然后每组测试数据先输入一个整数N,N <= 512且N是2的次幂,然后输入一个N*N的0,1矩阵。如果整个N*N的矩阵是全0(或者全1),则记录为00(或者01),不用继续拆分;否则,记录为1,并将矩阵四分为西北、东北、西南、东南各一块,并按此顺序检查各个分块是否全0或全1, 不是的话继续四分,最后把所有的01串连起来,转换为十六进制输出即可。

③可能很随便的一想就感觉先检查,然后四分,再检查再四分吧啦吧啦这样下去,但是如果某组矩阵维度过高则会使检查的时间开销大大增加,可能就TLE辽嘤嘤嘤嘤...所以我的思路是先把整个矩阵四分到不可再分,再逐一检查每个分叉,进行合并剪枝的操作,这样就避免了大面积检查浪费时间的情况。

④然后就是要把四分树中的0,1数字串用层次遍历的方法读取出来,再以四位为一个单位计算出十进制数并转化成十六进制输出。

⑤归结起来整体思路就是:

(1)深度优先生成四分树  (2)层次遍历四分树得到二进制数列  (3)将二进制数列转化为十六进制输出

⑥学到一个tip就是怎么输出八或者十六进制的数(详情看代码)


废话不多说了,上才艺!!!

#include<iostream>
#include<cstring>
#include<cstdio> 
#include<queue>
using namespace std;

class quadtree{  //四分树类 
	public:
		char value[3];  //取值"00","01","1"(其中00表示全0, 01表示全1, 1表示mixed 
		quadtree *child[4];
		quadtree(){
			child[0]=child[1]=child[2]=child[3]=0;
		} 
		bool operator ==(const quadtree& p) const{  //运算符重载 
			if(strcmp(value,"1")==0||strcmp(value,p.value)!=0) return 0;  //如果有孩子值为1或者存在相异值则不能合并 
			else return 1;
		}
		
}; 

quadtree *head;
char map[520][520],ans[10000],str[5];
int N,a[2500];

//组织输入并进行初始化 
void init(){  
	scanf("%d",&N);
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++)
			cin>>map[i][j];
	str[4]=0;
	memset(ans,0,sizeof(ans));
} 

//深度优先之四分树的四分,合并,剪枝 
quadtree * DFS(int r, int c, int len)//r ,c 坐标,len长度
{
	quadtree*temp = new quadtree;
	
	if(len==1){  //到四分到最小格时停止 
		temp->value[0]='0';  //第一个数 置0 
		temp->value[1]=map[r][c];  //第二个数则为方格中的数字 
		temp->value[2]=0;  //最后一位 置0 表示结束符 
		return temp;
	}
	
	len/=2;  //将map四分 
	temp->child[0]=DFS(r,c,len);
	temp->child[1]=DFS(r,c+len,len);
	temp->child[2]=DFS(r+len,c,len);
	temp->child[3]=DFS(r+len,c+len,len);
	
	bool flag=true;
	for(int i=1;i<4;i++)  //判断是否符合合并要求 
		if(!(*temp->child[0]==*temp->child[i])){ flag=false;break;}
	
	if(flag)  //若满足要求则合并 
	{
		strcpy(temp->value, temp->child[0]->value);
		for (int i = 0; i < 4; i++){
			delete temp->child[i];  //剪枝 
			temp->child[i]=0;
		}
	}
	else 
		strcpy(temp->value, "1");  //否则该节点不合并,并且节点值为1 
	return temp;
}

//将二进制转化为十进制数
void funtion(char s[]){
	int sum=0;
	for(int i=0;i<4;i++) sum=sum*2+str[i];
	printf("%X",sum);
}

//层次遍历取出二进制数字字符串,并转为十六进制输出
void print(){
	int i,j,m;
	
	quadtree *temp;
	queue<quadtree *> q;
	q.push(head);
	while(!q.empty()){
		temp=q.front();q.pop();
		strcat(ans,temp->value);  //将二进制串连接起来
		if(!(temp->child[0]==0))  //若该节点存在孩子
		 	for(i=0;i<4;i++) q.push(temp->child[i]);
		delete temp; 
	}
	
	int slen =strlen(ans);  //计算ans的长度 
	int pos=0;  //标记ans当前计算的位置 
	
	//为了方便计算,将整个数字串长度补成4的倍数
	//用长度求余数得i,在最前面补4-i个0
	if(slen%4!=0){
		i=slen%4;
		for(j=0;j<4-i;j++) str[j]=0;
		for(m=j;m<4;m++) str[m]=ans[pos++]-'0'; 
		funtion(str);
	}
	
	for(i=pos;i<slen;i+=4){
		for(j=0;j<4;j++) str[j]=ans[i+j]-'0';
		funtion(str);
	}
	printf("\n");
} 

int main(){
	int num;
	scanf("%d",&num);
	
	while(num--){
		init();
		head=DFS(0,0,N);
		print();
	}
	return 0;
}

 

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值