離散化
離散化,把無限空間中有限的個體對映到有限的空間中去,以此提高演算法的時空效率。
通俗的說,離散化是在不改變資料相對大小的條件下,對資料進行相應的縮小。例如:
原資料:1,999,100000,15;處理後:1,3,4,2;
原資料:,,;
處理後:,,;
離散化是程式設計中乙個常用的技巧,它可以有效的降低時間複雜度。其基本思想就是在眾多可能的情況中,只考慮需要用的值。離散化可以改進乙個低效的演算法,甚至實現根本不可能實現的演算法。要掌握這個思想,必須從大量的題目中理解此方法的特點。例如,在建造線段樹空間不夠的情況下,可以考慮離散化。
資料的離散化
有些資料本身很大, 自身無法作為陣列的下標儲存對應的屬性。如果這時只是需要這堆資料的相對屬性, 那麼可以對其進行離散化處理。當資料只與它們之間的相對大小有關,而與具體是多少無關時,可以進行離散化。
例 1:
91054 與 52143的逆序對個數相同。
例 2:
設有4個數: 1234567、123456789、12345678、123456
排序:123456<1234567<12345678<123456789
=>1<2<3<4
那麼這4個數可以表示成:2、4、3、1
例 3:
比如給你n個數:98998988,32434234,433234556,32434234,8384733,……
讓你統計其中每個數出現的次數,傳統的做法有好幾種,比如一遍一遍的掃過去,比對疊加,這樣演算法的效率是o(n2),效率低下;
再比如先排序,再統計連續的相同的個數,這裡的效率已經有所提高了,不過假如上面的資料是一道線段樹的題目給出的資料,那麼建樹需要的空間開銷實在是太大了。
再改進一下,採用雜湊的方法,開乙個大於其中最大數的陣列並初始化為零,o(n)掃一下,在該數字對應的下標的元素上+1,如果對於比較小的數字還好說,但是對於上面出現的數字直接採用雜湊對空間的開銷是十分大的也是沒有必要的,所以這裡用到了資料的離散化。
首先將數字排序:32434234,32434234,43324556,8384733,98998988
去重後給予其對應的索引: 0,0,1,2,3 (一一對映)
分別對應每個數,就可以簡化很多操作,減少了很多不必要的資源開銷。
除了對於較大整數需要使用離散化之外,對於一些需要使用整型資料結構,但給出的資料卻是小數的也可以使用離散化,將其索引為整數就可以了。
那麼可以總結出離散化的步驟:
1、排序
2、去重
3、索引
為了簡化**,我們採用stl演算法離散化:
/*
用離散化之前先用 sort()排序,再用 unique() 進行去重
用 lower_bound() 或者 upper_bound() 進行二分查詢位置
*/
int a[n], b[n], sub[n];
// a[n]是即將被離散化的陣列,b[n]是a[n]的副本,sub用於排序去重後提供離散化後的值
sort(sub, sub + n);
int size = unique(sub, sub + n) - sub;
for(int i = 0; i < n; i++)
a[i] = lower_bound(sub, sub + size, a[i]) - sub;
//即a[i]為b[i]離散化後對應的值
1、unique()函式————返回值是去重之後的長度
unique() 的作用是「去掉」容器中相鄰元素的重複元素(不一定要求陣列有序),即去重
它會把重複的元素新增到容器末尾(所以陣列大小並沒有改變),而返回值是去重之後的尾位址
如果要刪去重複元素,可以把尾巴刪去即可(或者直接定義新的長度!)
例如:
sz=unique(b+1,b+n+1)-(b+1);//減去的(b+1) 及 a 是起始位址
sz=unique(a,a+n)-a;
2、二分查詢——lower_bound()、upper_bonud()
/*
upper_bound(i) 返回的是鍵值為i的元素可以插入的最後乙個位置(上界)
lowe_bound(i) 返回的是鍵值為i的元素可以插入的位置的第乙個位置(下界)。
*/
怎麼理解呢,舉例:
在公升序的set裡面
set裡沒有元素i的時候,兩個元素的返回值是一樣的。
1 2 4 5 這個序列,upp(3)和low(3)都返回位置2(下標)
如果只有乙個元素i,low返回那個元素的位置,而upp返回那個元素的位置的後乙個位置。
1 2 4 5 這個序列upp(2)返回下標2而low(2)返回下標1
多個元素i,low返回那個元素的位置,upp返回那多個元素中的最後乙個的後乙個位置。
1 2 2 4 5 這個序列 upp(2)返回下標3的位置,low(2)返回下標1的位置。
!!!!!!!!!!!!!
特別注意:舉例在乙個公升序的容器裡,如果所有元素都大於i則,upp和low都返回begin。都小於i則返回end(越界了)。
最後再來一句,看是否好理解一些。
terator lower_bound( const key_type &key ): 返回乙個迭代器,指向鍵值》= key的第乙個元素。
iterator upper_bound( const key_type &key ):返回乙個迭代器,指向鍵值<=key的最後乙個元素的後乙個元素。
★降序排列的容器:
iterator lower_bound( const key_type &key ): 返回乙個迭代器,指向鍵值<= key的第乙個元素。
iterator upper_bound( const key_type &key ):返回乙個迭代器,指向鍵值》=key的最後乙個元素的後乙個元素。
例如:
bool cmp(int a,int b)
int main()
; sort(a,a+6,cmp); // 去重之前先排序
int m=unique(a,a+6)-a; // 去重
cout
//按從小到大 4 最多能插入陣列 a 的哪個位置
int p=lower_bound(a,a+6,4)-a;
//按從小到大,4最少能插入陣列 a 的哪個位置
cout
3 2
離散化 去重 二分查詢
離散化是程式設計中乙個常用的技巧,它可以有效的降低時間複雜度。其基本思想就是在眾多可能的情況中,只考慮需要用的值。離散化可以改進乙個低效的演算法,甚至實現根本不可能實現的演算法。要掌握這個思想,必須從大量的題目中理解此方法的特點。例如,在建造線段樹空間不夠的情況下,可以考慮離散化。1 排序 2 去重...
B Uniqueness(二分 離散化)
題意 問去掉最小多少的一段能使所有不同數字的個數為1。題解 以去掉的長度為基準進行二分,因為ai能到達1e9,所以還要離散化,然後直接暴力查詢即可。include include include include include include include include include incl...
區間和(離散化 二分 字首和)
假定有乙個無限長的數軸,數軸上每個座標上的數都是0。現在,我們首先進行 n 次操作,每次操作將某一位置x上的數加c。近下來,進行 m 次詢問,每個詢問包含兩個整數l和r,你需要求出在區間 l,r 之間的所有數的和。輸入格式 第一行包含兩個整數n和m。接下來 n 行,每行包含兩個整數x和c。再接下裡 ...