離散化是演算法競賽中常常要用到的一種技巧,經常會出現在一些資料結構的題目中,和一些資料結構結合起來。試想,假如你現在看到了一道線段樹的裸題,在你極其興奮的同時發現資料範圍是1-1e10的,開4倍的線段樹根本開不下。於是你極其懊惱地只拿了部分分,可能還會因為心情不好而出鍋爆零(逃)...
所以這裡來講一下離散化的原理及實現方式(嚴肅)
ps:對於離散化零概念同學,建議從頭翻到尾,如果只是來複習離散化的寫法,請直接到文尾。
首先,什麼是離散化?
在鄙人的理解中,離散化就是比相對大小。
為了本篇題解的正規性和學術性,先來貼一波標準定義:(滑稽)
離散化,把無限空間中有限的個體對映到有限的空間中去,以此提高演算法的時空效率。
通俗的說,離散化是在不改變資料相對大小的條件下,對資料進行相應的縮小。
看不懂就看下面的例子:
原數:131021 546412 973324
離散化後資料:1 2 3
夠形象了吧......
試想,當你要對乙個長度為\(10^\)的序列進行處理,你是否會開乙個如此之大的陣列?
除非你想mle,或者,你不會離散化。
在一些題目和演算法中,我們會發現,我們實現我們的想法的時候,只跟原資料的相對大小有關,比如1000000 和 2000000,在實際實現的時候,和1 2的效果是完全一樣的。那麼,開2000000那麼大的空間純屬浪費。那麼我們就用離散化給它對映到乙個較小的區間中。
通俗地來講,當有些資料本身很大, 自身無法作為陣列的下標儲存對應的屬性。如果這時只是需要這堆資料的相對屬性, 那麼可以對其進行離散化處理。當資料只與它們之間的相對大小有關,而與具體是多少無關時,可以進行離散化。
離散化的實現比較簡單。我們只需要維護兩個事情不變:首先:保證離散化之後的資料盡可能地小而且非負。其次:離散後的資料要保持原本的大小關係,原本相等的也要保持相等,否則就是錯誤的離散。
因此,找出原資料在序列中的序位就是離散化的關鍵。
我們在正常實現離散化的時候,有兩種方法:
結構體實現:
**:
struct node
a[maxn];
int rank[maxn],n;
-----------------------------
for(int i=1;i<=n;i++)
sort(a+1,a+n+1);//從小到大
for(int i=1;i<=n;i++)
rank[num[i].id]=i;//對映
解釋:
這實際上就是乙個結構體模擬對映的過程。
一開始輸入了原資料,並且按次序儲存了id,也就是原數列的位置。
然後進行排序,易知對映後的資料範圍就是1-n。所以排序後的位置就放上我們當前的i即可。
但是,這個方式有乙個弊端,就是不能處理資料相等的情況。如果碰到資料相等,那麼離散化之後就變成了不等。
陣列實現
**:
int a[maxn],b[maxn];
for(int i=1;i<=n;i++)
sort(b+1,b+n+1,cmp);//cmp函式是自定義比較從小到大或從大到小的
int size=unique(b+1,b+n+1)-(b+1);
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+size+1,a[i])-b;
解釋:
陣列實現離散化的碼量很少,減少了程式設計的複雜度。但是稍稍難理解一些(可能對讀者理解造成主要困難的就是不明白unique和lower-bound函式)。輸入a[i]之後緊接著儲存同樣的b[i]作為副本。然後對副本b進行去重,並儲存b陣列去重後的長度(size)。
然後開始離散化,直接把對應元素轉換成相應的陣列下標即可。
對unique和lower_bound函式不明白的同學請看下面:
針對上面那段**,已經被排好序的b陣列的下標就是我們要對映到的東西,我們就得到了乙個經過離散化之後的,去重的a陣列。
補充(upd:2020.3.16):也有一種寫法,是每次需要用a[i]的離散後數值時才查詢;個人不推薦這麼寫,因為當詢問次數很多的時候會大量消耗時間,而像上文那樣直接處理好的寫法,每次只需要o(1)的時間就可以完成查詢。
stl map實現(upd:2020.3.16)
stl為我們提供了乙個非常好的,可以處理對映問題的模板:map。
關於map容器,有不懂的小夥伴可以去翻:c++stl——map
模板:int a[maxn];
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1,cmp);
int cnt=0;
for(int i=1;i<=n;i++)
思想是一樣的,應用了乙個cnt變數來維護去重,呼叫的時候可以直接用鍵值,非常舒服方便。
校門外的樹(離散化寫法)
某校大門外長度為l的馬路上有一排樹,每兩棵相鄰的樹之間的間隔都是1公尺。我們可以把馬路看成乙個數軸,馬路的一端在數軸0的位置,另一端在l的位置 數軸上的每個整數點,即0,1,2,l,都種有一棵樹。由於馬路上有一些區域要用來建地鐵。這些區域用它們在數軸上的起始點和終止點表示。已知任一區域的起始點和終止...
資料離散化及其KMeans演算法實現的理論理解
k means演算法小結 資料離散化是資料預處理的乙個非常重要的步驟,就是將連續的資料分成幾個段。舉個簡單例子,好比我們乙個班上的學生成績是從0 100分之間的,但是我們在進行資料分析的時候呢我們把這些分數分成不及格 及格 良好 優秀四大類,實際上就是將比較連續的分數給離散化成了4種可能取值。那這樣...
離散化問題
題目傳送 uvalive 4127 the sky is the limit 大白書離散化簡單題。找了半天錯誤,居然是少輸出乙個空行。頓時感覺自己萌萌噠。其中計算幾何是套的之前留下的模板。ac include include include include include include inclu...