學習筆記--幾種離散化方式
前言
在OI學習過程中,我們常常會發現一些題目(尤其數據結構題)中,一些數據的範圍很大,但是涉及的數值的個數卻很少,同時我們想用一個數組的下標與這些數據建立一一對應關系,這時我們就需要離散化
大致思路
對於一個大小為\(N\)不含重復數字的數組\(a[N] (a[i]<=10^9)\),我們可以將\(a[]\)中的N個整數與\(1\) ~ \(N\)這\(N\)構成一一映射關系,也就是說把\(a[i]\)用一個\(1\)~\(N\)中的數字代替,這樣空間和時間復雜度都能變成與\(N\)相關
當然如果數組中有重復數據,你需要先去重(使用\(std::unique\)等)再進行上述操作
方式
結構體+\(sort\)
對於大小為\(N\)不含重復數據的整型數組\(a[N]\),定義結構體
struct Data{ int x;//原數組中的數據 int id;//原數組中儲存x的下標 }d[N];
然後以\(x\)為關鍵字進行排序,進行以下操作
sort(d+1,d+1+N);//假設從1開始 for(int i=1;i<=n;i++){ a[d[i].id]=i;//按大小順序離散化 }
時間復雜度\(O(NlogN)\) 空間復雜度\(O(N)\)
但是,使用這種方式的前提是數組無重復數據
\(sort\)+\(lower\_bound\)
這應該是最常見的離散化方式
您只需要知道對於大小為\(N\)的數組\(a[]\)
\(lower\_bound(a+1,a+1+N,X)-a\)返回\(a\)中第一個大於等於X的位置
\(unique(a+1,a+1+n)-(a+1)\)返回將\(a\)數組去重後\(a\)的數組大小
然後就不難理解下面代碼
for(int i=1;i<=N;i++){ scanf("%d",&a[i]); f[i]=a[i]; } sort(f+1,f+1+N); int nn=unique(f+1,f+1+N)-(f+1);//去重 for(int i=1;i<=N;i++){ a[i]=lower_bound(f+1,f+1+nn,a[i])-f; }
這樣\(f[i]\)
\(map\&unordered\_map\)
如果您不知道STL中的\(map\),建議您先去了解再來看此篇文章
其實思路很\(naive\),知道\(map\)用法的應該都能看懂
map <int,int> g; int a[N],f[N],tot=0; for(int i=1;i<=N;i++){ scanf("%d",&a[i]); if(!g[a[i]]){ g[a[i]]=++tot; f[tot]=a[i]; } a[i]=g[a[i]]; }
\(f[a[i]]\)就是原數組\(a[i]\)的值
但是\(map\)是用紅黑樹實現的,儲存的元素是有序的
而\(unordered\_map\)是用哈希表實現的
而在這裏\(map\)純粹只是起到了\(hash\)的查找與賦值,用\(unordered\_map\)也能實現,相比較之下\(unordered\_map\)一般會更快一點(實際上您可以手寫一個哈希表完成上面的離散化操作)
然而使用\(unordered\_map\)時註意,\(C++11\)之前使用需要
#include <tr1/unordered_map> using namespace std; using namespace std::tr1; unordered_map <int,int>g;
\(C++ 11\)之後則可以使用
#include <unordered_map> using namespace std; unordered_map<int,int>g;
\(pb\_ds\)中的\(hash\_table\)
\(pb\_ds\)中有許多黑科技,您可以在這篇博客中了解:
https://blog.csdn.net/Only_AiR/article/details/51940500
其中就有個\(hash\_table\),顧名思義,就是個蛤希表了,可以只用\(find()\)和\(operator[]\),十分方便
然而使用它需要記一點東西,但你問我資不資瓷,當然是資瓷啊
#include <ext/pb_ds/assoc_container.hpp> #include <ext/pb_ds/hash_policy.hpp> using namespace __gnu_pbds; cc_hash_table <int,int>g1;//兩種hash_table,都跑得和香港記者一樣快 gp_hash_table <int,int>g2;
後記
話說寫這篇博客還是因為這道題
https://www.lydsy.com/JudgeOnline/problem.php?id=4241
我調整各種離散化方式來看看哪個最快,同時在luogu的個人私題中同步測試
然後給大家看看時間比較(因為怕影響大家評測把時間限制開的很小,難免會TLE,\(bzoj\)時限是80s)
\(hash\_table\)不知道高到哪裏去
然而戲劇性的是BZOJ 上我測出來是\(map\)最快!!! \(18000+ms\)
其余的都比裸\(map\)慢了近\(1000\)~\(2000+\) $ $ \(ms\)
很奇怪,BZOJ評測鴨太玄學了,如果有誰知道原因的可以解釋下謝謝
學習筆記--幾種離散化方式