1. 程式人生 > >學習筆記--幾種離散化方式

學習筆記--幾種離散化方式

常見 然而 stl namespace img 完成 uno find 之前

前言

在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]\)

    儲存了從小到大排序後原來\(a\)中所有元素,\(a[i]\)中就儲存了按大小排序後,原本\(a[i]\)大小的排名,\(f[ \ a[i] \ ]\)則返回原本\(a[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評測鴨太玄學了,如果有誰知道原因的可以解釋下謝謝

學習筆記--幾種離散化方式