[NOIP系列] 离散化

NOIP系列

c++中离散化是什么?

先看一到例题

给定 n 个数(可能相同),出现次数最多的数出现了多少次。(n<= 10 ^ 9)

嗯……这道题看似一道水题,只要开一个v 数组记录每一个数字出现的次数即可,比如 v[a[i]]++。但是值得注意的是,ai可能非常大,这就导致 vis 数组会开不下,因此,就要用到离散化

大体来说,离散化就是将一串数字进行压缩
比如 g[1] = 1 g[2] = 3 g[3] = 100000 g[4] = 45
将其离散化后, g[1] = 1 g[2] = 2 g[3] = 4 g[4] = 3,此时 g[i] 存的是每一个数在所有数的大小位置。上述中,45 是第三大的,所以原来存 45 的 g[4] 就变成了3。这样例题就好办了,因为此时的 g 就相当于 vis 数组,而这个数组最大长度一定小于等于 n。(可能有重复数字)对了,若想保留原来的数的话,只需另开一个数组记录。

那离散化具体怎么实现呢?

这里面讲两种做法

1.

首先我们需要开一个结构体来记录每一个数的初始值和在数组中的位置。然后创建一个指针,这个指针用来表示不同数字的个数。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, a[10005], g[10005];    //g[] 来存值 
struct node
{
    int num, id;
    bool operator < (const node& other)const
    {
        return num < other.num;        //默认从小到大 
    }
}t[maxn];
int now = 0;
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    for(int i = 1; i <= n; ++i){t[i].num = a[i]; t[i].id = i;}
    sort(t + 1, t + n + 1);        //要离散,先排序 
    for(int i = 1; i <= n; ++i)
    {
        if(i == 1 || t[i].num != t[i].num) now++;    //如果是和前面不同的数,now++(因为已经排好序了) 
        g[now] = t[i].num; a[t[i].id] = now;        //此时的a[i]存的就是原来a[i]是第几小的数 
    }
    for(int i = 1; i <= n; ++i) printf("%d ", a[i]);
    printf("\n");
    for(int i = 1; i <= n; ++i) printf("%d ", g[i]);
    printf("\n");
    return 0;
}

输入:

5

2 100 4 1 4

输出:

2 4 3 1 3

1 2 4 100

此时我们还能发现,指针 now 不仅表示了有 now 个不同的数,也表示了第 now 小的数是什么

稍微改一下,就是例题

#include <bits/stdc++.h>
using namespace std;
int n, a[100005], g[100005], tot[100005];    
bool cmp(int a, int b)
{
    return a > b;
}
struct node
{
    int num, id;
    bool operator < (const node& other)const
    {
        return num < other.num;    
    }
}t[maxn];
int now = 0;
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    for(int i = 1; i <= n; ++i){t[i].num = a[i]; t[i].id = i;}
    sort(t + 1, t + n + 1);        
    for(int i = 1; i <= n; ++i)
    {
        if(i == 1 || t[i].num != t[i - 1].num) now++;    
        g[now] = t[i].num; a[t[i].id] = now;        
    }
    for(int i = 1; i <= n; ++i) tot[a[i]]++;
    sort(tot + 1, tot + n + 1, cmp);
    printf("%d\n", tot[1]);
    return 0;
}

2.

这种做法是我前最近学到的,不仅想法简单,而且实现起来也很方便。

首先我们将所有数存到一个数组中,然后排个序,再去个重(用unique函数),最后用lower_bound查找每一个数的位置,即离散化后的值。

#include <bits/stdc++.h>
using namespace std;
int a[100005], t[100005];
int main()
{
    int n; scanf("%d", &n); 
    for(int i = 1; i <= n; ++i) {scanf("%d", &a[i]); t[i] = a[i];}
    sort(a + 1, a + n + 1);
    int _n = unique(a + 1, a + n + 1) - a - 1;         
    for(int i = 1; i <= _n; ++i)
        t[i] = lower_bound(a + 1, a + _n + 1, t[i]) - a;
    for(int i = 1; i <= n; ++i) printf("%d ", t[i]);
    return 0;
}

输入:

7

1 2 8 4 6 4 100

输出:

1 2 5 3 4 3 100

最后,我们还是要强调一下,离散化,请务必掌握!!!

文章来源:https://me.csdn.net/qq_43058710

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值