zoukankan      html  css  js  c++  java
  • 离散化

    离散化

    问题模型


    离散化是指对于数据范围较大(1e9),但数的个数较少(1e5)的一些数进行操作(并查集等)时,需要通过比较这些数的相对大小,将其离散化,得到"1e5"以内的“离散化数值”,然后就可以将其储存在数组中,进行并查集等操作了。


    实现原理

    当离散化的数据无重复时,可以通过记录数组下标,直接在结构体内进行排序等操作

    update 于2019.8.15

    bool cmp(int x,int y)
    {
        return a[x]>a[y];
    }
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);b[i]=i;
    }
    sort(b+1,b+n+1,cmp);
    for(int i=1;i<=n;i++)
        rank[b[i]]=i;
    

    当离散化的数据存在重复时,可以通过先排序(nlogn),再去重(因排序后数据有序,故复杂度从O(n^2)降到O(n),最后通过二分查找到数据的相对大小(离散化后的值)。

    代码实现

    数据无重复(略)

    数据有重复:

        int tot=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i].a1,&a[i].a2);san[++tot]=a[i].a1;san[++tot]=a[i].a2;
        }
        sort(san+1,san+tot+1,cmp1);
        m=unique(san+1,san+tot+1)-(san+1);//m表示去重后的数值大小,unique并未删除数据,只是将数据放在了数组末尾,因数据有序,复杂度为O(n)
        for(int i=1;i<=n;i++)
        {
            a[i].a1=query(a[i].a1);//query:二分查找(表示不会lower_bound,upper_bound,只能手打)
            a[i].a2=query(a[i].a2);
        }
    

    例题

    洛谷P1439 [模板]最长公共子序列

    解析:看似是道裸LCS,观察数据后发现n<=100000,n^2过不了,只能nlogn过。其实这道题因为排列的性质,可以通过离散化,将问题转化为LIS,再加个LIS的优化就能过了。举个栗子吧:样例中的3 2 1 4 5分别被赋予1 2 3 4 5的离散化值,1 2 3 4 5对应的离散化值就为3 2 1 4 5,然后再求3 2 1 4 5的LIS,即1 4 5,则最长公共子序列的长度为3。正确性证明:离散化值如果递增,则再原排列中所在位置也单调递增。如果是求最长下降子序列,则再原排列中的位置是逆序的,不符合题意。

    #include <iostream>
    #include <cstdio>
    using namespace std;
    #define Maxn 100100
    int n;
    int a[Maxn],b[Maxn];
    int po[Maxn],situ[Maxn];
    int dp[Maxn];
    int h[Maxn];
    int maxx=1;
    int query(int x)
    {
        int l=0,r=maxx;int ans=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(h[mid]<=x)
            {
                ans=mid;l=mid+1;
            }
            else r=mid-1;
        }
        return ans;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),po[a[i]]=i;
        for(int i=1;i<=n;i++)scanf("%d",&b[i]),situ[i]=po[b[i]];
        h[0]=-2e9;h[1]=situ[1];dp[1]=1;
        for(int i=2;i<=n;i++)h[i]=2e9;
        for(int i=2;i<=n;i++)
        {
            int tmp=query(situ[i]);
            if(tmp==maxx){maxx++;h[maxx]=situ[i];dp[i]=maxx;}
            else{h[tmp+1]=min(h[tmp+1],situ[i]);dp[i]=tmp+1;}
        }
        int ans=0;
        for(int i=1;i<=n;i++)ans=max(ans,dp[i]);
        printf("%d",ans);
        return 0;
    }
    
    

    [NOI2015]程序自动分析(做过的最水的一道NOI题目

    解析:并查集+数据存在重复的离散化

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    #define Maxn 1000100
    int n;
    struct node{
       int a1,a2,com;
    }a[Maxn];
    int san[Maxn*2];int tot=0;int m;
    int query(int x)
    {
        int l=1,r=m;int ans=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(san[mid]>=x)
            {
                ans=mid;r=mid-1;
            }
            else l=mid+1;
        }
        return ans;
    }
    bool cmp1(int x,int y){return x<y;}
    bool cmp2(node x,node y){return x.com>y.com;}
    int fa[Maxn*2];
    int find_fa(int x)
    {
        return fa[x]==x?x:fa[x]=find_fa(fa[x]);
    }
    int main()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
        scanf("%d",&n);
        for(int i=1;i<=n*2;i++)san[i]=0;
        tot=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d",&a[i].a1,&a[i].a2,&a[i].com);san[++tot]=a[i].a1;san[++tot]=a[i].a2;
        }
        sort(san+1,san+tot+1,cmp1);
        m=unique(san+1,san+tot+1)-(san+1);
        for(int i=1;i<=n;i++)
        {
            a[i].a1=query(a[i].a1);
            a[i].a2=query(a[i].a2);
        }
        sort(a+1,a+n+1,cmp2);
        for(int i=1;i<=n*2;i++)fa[i]=i;
        int pd=0;
        for(int i=1;i<=n;i++)
        {
            int fa1=find_fa(a[i].a1);
            int fa2=find_fa(a[i].a2);
            if(a[i].com==1)fa[fa1]=fa2;
            else
            {
                if(fa1==fa2)pd=1;
            }
            if(pd)break;
        }
        if(pd)printf("NO
    ");else printf("YES
    ");
        }
        return 0;
    }
    
    
  • 相关阅读:
    A1023 Have Fun with Numbers (20分)(大整数四则运算)
    A1096 Consecutive Factors (20分)(质数分解)
    A1078 Hashing (25分)(哈希表、平方探测法)
    A1015 Reversible Primes (20分)(素数判断,进制转换)
    A1081 Rational Sum (20分)
    A1088 Rational Arithmetic (20分)
    A1049 Counting Ones (30分)
    A1008 Elevator (20分)
    A1059 Prime Factors (25分)
    A1155 Heap Paths (30分)
  • 原文地址:https://www.cnblogs.com/Akaina/p/11225119.html
Copyright © 2011-2022 走看看