離散化實現|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

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