每天学一丢之 坐标离散化 (4. 24)

每天学一丢意为每天学一点丢一点。

然后每天二字名存实亡,还是坚持在每天学习,只是每天学习的东西不够单开一文去总结。

其实第二个本来打算写LCA的,但是LCA还没学明白加上今天刚刚又口胡一遍坐标离散化,那就总结一下吧。


坐标离散化,适用于一个较大的图中,有很多无用的部分,这时我们通过离散化的方式,将他们缩小并保持原关系不变。

首先给出一个一维离散化的例子。给出数轴N(N<1e5, val(N)<1e15)个点,假设是1,10,500,3700,3701,4200,80000, 10000000000。我们如果开一个布尔型数组,并且用他的true or false来表示这个点是不是我们要的点,要开的数组开不了那么大的。那么我们一般会怎么做?

开一个数组或者vector,然后按照大小顺序把这N个点存进去就可以了,我们就基本表示了这几个数的大小关系,这个时候我们换一种理解方式,我们是不是把N个值很大的点,离散到了数轴上的前N个点,并按照大小关系一一对应。

有些抽象,好的我们再给出一个二维的可视的例子


这个图的原题题意是求白色的四联通块个数,那我们只需要记录下原来图的大致形状,不需要知道每块的大小。那我们类比一下一维的离散化,“我们不需要数轴上所有点,我们只需要给定的N个点”,那么在这样一个二维的图中,我们不需要所有的格子,我们只需要黑色的线所分割开的白色块的大致形状,即我们只需要保留黑色线和黑色线的前后各一条线。然后转化为我们保留端点所在行列和上下左右行列。然后就得到了我们离散化后的第二个图。如果说这样还是不知道为什么的话,我也不知道我该怎么解释,所以那就这样吧。我懂就好。

然后给出一个坐标离散化的代码,代码是对二维空间中的N个点进行离散化。

const int maxn = 205;
int x[maxn], y[maxn];       //用于存储N个点的横纵坐标

int LSH(int *x, int N, int R)  //N个点 x[]储存横坐标信息 原图边界为[1, R]
{

    vector<int > xs;

    for(int i=0; i<N; i++)
        for(int d=-1; d<=1; d++)
            if(x[i]+d>=1 && x[i]+d<=R)  //保留在原图范围内的,该点所在行和上下两行信息(列也相同)
                xs.push_back(x[i]+d);

    sort(xs.begin(), xs.end());         //对xs排序,为之后的unique做准备
    xs.erase(unique(xs.begin(), xs.end()), xs.end());   //先将xs去重排序,后将重复部分删去

    for(int i=0; i<N; i++)
        x[i] = find(xs.begin(), xs.end(), x[i]) - xs.begin();   //find返回的是迭代器,做减法后取得的是离散化之后的坐标
                                                                //即:将原坐标转为下标信息储存起来,保留了原坐标之间的大小关系

    return xs.size();       //返回的是离散化之后图的边界
}

从这里我们可以看出,坐标离散化其实就是我们对我们需要保留的坐标信息(例中为横坐标以及周围两行),进行处理,使得离散化之后的坐标中保留原来的大小关系,并省略所有不必要的坐标信息。

再给出一种二维空间下,带权离散化的例子,带权离散化可以记录下原图各部分的大小关系,即对应了离散化之后的点相当于原本多少个点的“浓缩”

int LSH(int *x, int* xx, int R)
{
    vector<int >xs;
    for(int i=0; i<n; i++)
        for(int d=-1; d<=1; d++)
            if(x[i]+d>0 && x[i]+d<=R)
                xs.push_back(x[i]+d);                                  ///将对应坐标本身和左右读入到xs中,即xs中储存的是我们所有需要保留的坐标值

    sort(xs.begin(), xs.end());                                         ///先sort才能用unique
    xs.erase(unique(xs.begin(), xs.end()), xs.end());                   ///unique将区间中重复的元素放到数组的最后部分,并返回第一个重复元素的指针(迭代器)
                                                                        ///然后用erase删去重复部分,即xs中按照升序存放了所有要用到的坐标,而xs的下标就是离散化之后对应的坐标
    for(int i=0; i<n; i++){
        int sign = find(xs.begin(), xs.end(), x[i]) - xs.begin() + 1;    ///新的下标  [1, xs.size()]
        x[i] = sign;                                                     ///x[i]对应到新的下标
    }

    for(int i=0; i<xs.size(); i++){                                      ///新的下标所对应的原值,用于判断离散化后节点对应的权值
        xx[i+1] = xs[i];
    }

    xx[0] = 0;                                                           ///最小边界为0
    xx[xs.size()+1] = R+1;                                               ///最大行为R+1行
    return xs.size();
}

和上一例不同的是,此例中我们将离散化之后的行信息,对应原图中的多少行储存在了xx[]数组中,那么我们就可以利用相邻两行所代表的原行数来求出两行之间省略了多少行,那么对于任意的一个格子,我们就可以对应得到原图中它代表了多少格。

LL x1 = nowx==1?xx[nowx+1]-xx[nowx-1]-1:xx[nowx+1]-xx[nowx];    ///将省略部分全部给了较小的一行(列)所以要对第一行(列)进行特殊处理
LL y1 = nowy==1?yy[nowy+1]-yy[nowy-1]-1:yy[nowy+1]-yy[nowy];
cnt = x1*y1;                          ///cnt代表当前单元格所代表的行列乘积,即原图中代表的格点数

在这个例子中,我们人为的省略的部分全部给了左边的格点,那么最靠前位置省略的格点就给了图外部分,所以需要特殊判断是不是第一行或者第一列,然后再去计算。

以上就是我学到的关于坐标离散化的知识,如果有更高深的应用,鬼知道我会不会更新

猜你喜欢

转载自blog.csdn.net/ShadowGhostH/article/details/80068825