1. 程式人生 > >常用技巧——離散化

常用技巧——離散化

提高效率 原本 例如 bound 簡單 span 下標 範圍 功能

“離散化,就是把無限空間中有限的個體映射到有限的空間中去,以提高算法的時空效率。”

很多算法的復雜度與數據中的最大值有關,比如樹狀數組和純用數組實現的一對一標記。時常會遇到這種情況:數據的範圍非常大或者其中含有負數,但數據本身的個數並不是很多(遠小於數據範圍)。在這種情況下,如果每個數據項的具體值並不重要,重要的是他們之間的大小關系的話,我們可以先對這些數據進行離散化,使數據中的最大值盡可能小且保證所有數據都是正數。

例如,有這樣一個長為5的序列:102131511,123,9813186,-611,55。其中有非常大的數以及負數,會給許多算法的實現帶來困擾,我們可以把這個序列離散化,使它變成這樣:5,3,4,1,2。各個元素間的大小關系沒有任何改變,但數據的範圍一下子就變得很舒服了。

離散化的原理和實現都很簡單。為了確保不出錯且盡可能地提高效率,我們希望離散化能實現以下幾種功能:1.保證離散化後的數據非負且盡可能的小2.離散化後各數據項之間的大小關系不變,原本相等的也要保持相等。由此,找出數據項在原序列中從小到大排第幾就是離散化的關鍵。

可以通過下面的方法以O(nlong)的時間復雜度完成離散化,n為序列長度。

  1. 對原序列進行排序,使其按升序排列。
  2. 去掉序列中重復的元素。
  3. 此時序列中各位置的值和位置的序號就是離散化的映射方式。

例如:對於序列105,35,35,79,-7,排序並去重後變為-7,35,79,105,由此就得到了對應關系-7->1, 35->2, 79->3, 105->4。

代碼如下:

int n, a[maxn], t[maxn];
//這裏以下標1為序列的起點,一般情況下從0開始也可以
for(int i = 1;i <= n;i++)
{
    scanf("%d", &a[i]);
    t[i] = a[i];//t是一個臨時數組,用來得到離散化的映射關系
}
sort(t + 1, t + n + 1);//排序
int m = unique(t + 1, t + 1 + n) - t - 1;//去重,並獲得去重後的長度m
for(int i = 1;i <= n;i++)
{
    a[i] = lower_bound(t + 1, t + 1
+ m, a[i]) - t;//通過二分查找,快速地把元素和映射對應起來 }

常用技巧——離散化