离散化实现|bug分析及解决

离散化:

对于一些应用实例,有时只会用到数据的相对大小,而不在意数据本身的大小

例:在区间涂色问题中,依次给区间涂色,后涂色的区间会覆盖前区间。现在求剩下几种颜色

可以发现,这个问题中,有用的就只是区间的相对位置关系,而不在于区间本身的大小。如:

[1, 3] 涂白色,[6, 7] 涂黑色

[1, 3] 涂白色,[10000006, 10000007] 涂黑色

最后都只有两种颜色。但是若用线段树维护,第一种情况要区间长度 7,第二种情况却需要长度 10000007,太浪费时间空间

这里离散化就派上用场了。离散化就是把很大的区间,映射到小的区间中

用到的STL函数:

  • sort():排序。若原数据是无序的,就需要先排序之后再处理
  • unique():把相邻重复元素放到末尾,用不重复元素补上,返回不重复元素末尾地址。若原数据有序,则不重复元素也有序
  • lower-boud():返回第一个不小于指定值的地址
离散数组,左端点,右端点
int disc[MAXN], L[MAXN], R[MAXN]; 

读入数据
for (int k = 0; k < n; k++) {          
    scanf("%d%d", L + k, R + k);
    disc[tot++] = L[k];
    disc[tot++] = R[k];
}

若原数据无序,则需要排序后再去重
sort(disc, disc + tot);

去重,得到不重复元素的个数。此时数组中前面的不重复元素保持有序
int size = (int)(unique(disc, disc + tot) - disc);

进行映射,拿该元素在disc数组中的位置作为映射后的新下标
for (int i = 0; i < n; i ++) {
     L[i] = (int)(lower_bound(disc, disc + size, L[i]) - disc + 1);
     R[i] = (int)(lower_bound(disc, disc + size, R[i]) - disc + 1);
}

离散化bug:

这样离散的方式存在bug,我们举个例子

[1, 10]、[1,6]、[7,10]  ,剩下 2 种颜色

[1, 10]、[1,6]、[9,10]  ,  剩下 3 种颜色

但是离散化之后,区间全都变成

[1, 4]、[1,2]、[3,4]  ,剩下 2 种颜色,很明显有一些情况会出错

分析:

我们维护了一些区间之后,存储的不仅仅是哪些区域有颜色。从另一个角度看,同时也反映了哪些区间是空白的

如:[1,6]、[9,10] 两个区间,就告诉了我们 [7, 8] 区间是空白的

然而,离散化的处理,就是把靠近的两个数设成相邻,把它们之间的空间设为 0 ,这样会丢失空白的那部分数据

本身 6 与 9 之间有空白,离散化之后,变成 2 与 3。这与 6、7 的离散结果无异,空白信息被丢失,对结果造成影响

解决:

有两种解决方案

1、加点法:若两个相邻的数的差值大于 1 ,也就是说两数之间有空白,就在它们之间插入一个点,用来体现空白

实现代码:

int disc[MAXN], L[MAXN], R[MAXN]; 

for (int k = 0; k < n; k++) {      //读入数据
    scanf("%d%d", L + k, R + k);
    disc[tot++] = L[k];
    disc[tot++] = R[k];
}
sort(disc, disc + tot);  //若原数据无序,则需要排序后再去重
int size = (int)(unique(disc, disc + tot) - disc);   //去重,得到不重复元素的个数。此时数组中前面的不重复元素保持有序
int temp = size;      //加点,修复离散bug
for (int i = 1; i < temp; i++)
    if (disc[i] - disc[i - 1] > 1)
        disc[size++] = disc[i - 1] + 1;        
sort(disc, disc + size);
for (int i = 0; i < n; i ++) {    //进行映射,拿该元素在disc数组中的位置作为映射后的新下标
     L[i] = (int)(lower_bound(disc, disc + size, L[i]) - disc + 1);
     R[i] = (int)(lower_bound(disc, disc + size, R[i]) - disc + 1);
}

2、在维护线段树时,改变左右子树的划分方式

原来是: [l, mid]、[mid + 1, r]    改为:[l, mid]、[mid, r]   便可以解决

例题:https://blog.csdn.net/Originum/article/details/82951924

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章