POJ2528 Mayor's poster 线段树+离散化解法

标准解法是用的离散化。此题的特殊性在于普通离散化后,两个相邻的值之间的区间也可能是有效的区间。例如:

有三张海报[1, 4] [5, 6][1, 6],离散化后1->1, 2->4, 3->5, 4->6。他们就会覆盖[1, 6]这张海报。
另外三张海报[1, 3] [5, 6][1, 6],离散化后1->1, 2->3, 3->5, 4->6。他们不会覆盖[1, 6]这张海报。
而此时两组数据的前两张海报都会变成[1, 2] [3, 4] 两个区间,都覆盖了[1,4]区间。

解决方案是如果发现两个离散值之间的距离大于1,就在两个值之间插入一个数字。例如第二组中的3和5,他们大于1,就插入一个数,为了保证最后用二分查找的方便,插入(3 , 5)之间的任意数字都可以。

另外最开始的时候,我采用的动态生成数组存储离散化的值,最后导致了段错误(SIGSEGV)。采取使用静态数组,并且把部分的数组移到main函数外面,就可以了。

结果是:4476kB50ms

可以看出,不管是时间复杂度还是空间复杂度都比插入节点建树的方案好,而且自己实现的quicksort比系统的快,只是编码量比较大

#include <stdio.h>
#include <string.h>
//#include <stdlib.h>//qsort头文件
//#include <algorithm>//sort头文件
#include <iostream>
using namespace std;
struct Poster{
    int l,u;
};
struct InTree{
    int l,u;
    InTree *left,*right;
    int color;//-2表示该区间是混合色或者还未涂色
    InTree(int low,int upp){
        l= low; u = upp;
        left = right = 0;
        color = -2;
    }
};

InTree* Build(int low, int upp){
    InTree *root = new InTree(low,upp);
    if(low < upp){
    //递归构建左右子树,并初始化max和min值
        root->left = Build(low,(low+upp)/2);
        root->right = Build((low+upp)/2+1,upp);
    }
    return root;
}

void Update(InTree *root,int low,int upp,int c){
    if(low <= root->l && root->u <=upp){
        root->color = c;
        return;
    }
    else{
        if(root->color >= 0){//当本区间是单一色时,需要将颜色下推
            root->right->color = root->left->color = root->color;
            root->color = -2;
        }

        if(low <= (root->l+root->u)/2){
            Update(root->left,low,upp,c);
        }
        if(upp >= (root->l+root->u)/2 + 1){
            Update(root->right,low,upp,c);
        }
    }
}

void Query(InTree *root,bool *map){
//查询root子树中全涂成一种颜色的区间,并将区间的颜色对应的map位置置为true
    if(root == 0)
        return;
    else if(root->color >=0)
        map[root->color] = true;
    else{
        Query(root->left,map);
        Query(root->right,map);
    }
}

void Delete(InTree *root){
//递归删除节点
    if(root == 0)
        return;
    else{
        Delete(root->left);
        Delete(root->right);
        delete root;
    }
}

int CMP(const void *a,const void *b){
    int* ca = (int *)a;
    int* cb = (int *)b;
    if(*ca > *cb)return 1;
    if(*ca < *cb)return -1;
    if(*ca==*cb)return 0;
}

int BinarySearch(int *a,int n,int key){
    int l = 1,u = n;
    while(l<=u){
        int mid = l+(u-l)/2;
        if(a[mid] == key)
            return mid;
        else if(a[mid]<key)l = mid +1;
        else u = mid - 1;
    }
    return -1;
}

int Partition(int *pos,int n){
    int p = pos[0];
    int i=0,j=n;
    while(true){
        while(pos[++i]< p && i<n-1);
        while(pos[--j]> p);
        if(i>=j)break;
        else{
            int t = pos[i];
            pos[i] = pos[j];
            pos[j] = t;
        }
    }
    pos[0] = pos[j];
    pos[j] = p;
    return j;
}

void Quicksort(int *pos,int n){
    if(n>1){
        int i = Partition(pos,n);
        Quicksort(pos,i);
        Quicksort(pos+i+1,n-i-1);
    }
}

//如果把这两个数组写在main函数中,会出现rte的错误。估计是代码段容量不够。同理如果都写成new形式,也会容量不够
int pos[10010*2];
int indexs[10010*3];//最多3*n个位置(用index做名字会出错)

int main(){
    int c;
    int n;
    //int pos[10010*2];
    //int indexs[10010*3];//最多3*n个位置(用index做名字会出错)
    bool map[10010];
    Poster posters[10010];
    scanf("%d",&c);
    while(c>0){
        --c;
        scanf("%d",&n);
        //printf("n=%d;",n);
        int l,u;
        for(int i=0;i<n;i++){
            scanf("%d%d",&l,&u);
            pos[2*i] = posters[i].l = l;
            pos[2*i+1] = posters[i].u = u;
            //printf("l=%d;u=%d;\n",l,u);
        }
        //离散化端点值为排序后的下标
        //qsort(pos,2*n,sizeof(int),CMP);
        //sort(pos,pos+2*n);
        Quicksort(pos,2*n);
        //整理端点值,把相等的保留一个,把不相邻的两个端点之间插一个任意数
        int cnt = 0;//在indexs数组中记录从indexs[1]开始,cnt标识前一个有效位置
        indexs[++cnt]=pos[0];
        for(int i = 1;i<2*n;i++){
            if(pos[i] != indexs[cnt]){
                if(pos[i] > indexs[cnt]+1){//大于1,需要插入一个节点
                    indexs[++cnt] = pos[i]-1;
                }
                indexs[++cnt] = pos[i];
            }
        }

        //重新调整poster的端点坐标到[1,cnt]的区间
        for(int i=0;i<n;i++){
            posters[i].l = BinarySearch(indexs,cnt,posters[i].l);
            posters[i].u = BinarySearch(indexs,cnt,posters[i].u);
        }
        //建树
        InTree* root = Build(1,cnt);
        //更新数据
        for(int i=0;i<n;i++){
            Update(root,posters[i].l,posters[i].u,i);
        }
        //查询
        memset(map,false,10010*sizeof(bool));
        Query(root,map);
        //统计结果
        cnt=0;
        for(int i=0;i<n;i++){
            if(map[i])cnt++;
        }
        printf("%d\n",cnt);
        //删树
        Delete(root);
    }
    return 0;
}



posted @ 2012-10-19 22:31  moonswap  阅读(252)  评论(0编辑  收藏  举报