四分树

题目:openjudge 612

http://cqsyz.openjudge.cn/hanjai/9/

总时间限制: 
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进制编码。
样例输入
 
 
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

样例输出
0
114
258C0511


#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
int map[520][520];
bool check(int x,int y,int size){
    for(int i=x;i<x+size;i++)
        for(int j=y;j<y+size;j++)
            if(map[i][j]!=map[x][y])
                return false;
    return true;
}
typedef struct node{
    bool f;
    int x;
    node *c[2][2];
}node;
void extend(node* r,int x,int y,int size){
    size/=2;
    node *p;
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++){
            if(check(x+i*size,y+j*size,size)){
                p=new node;
                p->f=true;
                p->x=map[x+i*size][y+j*size];
                memset(p->c,NULL,sizeof(p->c));
                r->c[i][j]=p;
            }else{
                p=new node;
                p->f=false;
                p->x=1;
                r->c[i][j]=p;
                extend(p,x+i*size,y+j*size,size);
            }
        }
}
int main(){
    int k,n;
    cin>>k;
    while(k--){
        cin>>n;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                cin>>map[i][j];
        node *root;
        if(check(0,0,n)){
            root=new node;
            root->f=true;
            root->x=map[0][0];
            memset(root->c,NULL,sizeof(root->c));
        }else{
            root=new node;
            root->f=false;
            root->x=1;
            extend(root,0,0,n);
        }
        queue<node*> q;
        q.push(root);
        string s="";
        while(!q.empty()){
            node *p = q.front();
            q.pop();
            if(p->f){
                s+="0";
                s+='0'+p->x;
            }else{
                s+="1";
                for(int i=0;i<2;i++)
                    for(int j=0;j<2;j++)
                        q.push(p->c[i][j]);
            }
        }
        int l=s.size()%4;
        if(l!=0){
            for(int i=0;i<4-l;i++)
                s="0"+s;
        }
        string s2="";
        for(int i=0;i<s.size();i+=4){
            int x=0;
            for(int j=0;j<4;j++)
                x+=(s[i+j]-'0')<<(3-j);
            switch(x){
                case 10:
                    s2+='A';
                    break;
                case 11:
                    s2+='B';
                    break;
                case 12:
                    s2+='C';
                    break;
                case 13:
                    s2+='D';
                    break;
                case 14:
                    s2+='E';
                    break;
                case 15:
                    s2+='F';
                    break;
                default:
                    s2+='0'+x;
                    break;
            }
        }
        cout<<s2<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值