离散化
先看一到例题
给定 n 个数(可能相同),出现次数最多的数出现了多少次。(ai <= 10 ^ 9)
嗯……这道题看似一道水题,只要开一个 vis 数组记录每一个数字出现的次数即可,比如 vis[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.
首先我们需要开一个结构体来记录每一个数的初始值和在数组中的位置。然后创建一个指针,这个指针用来表示不同数字的个数。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int maxn = 1e5 + 5; 8 int n, a[maxn], g[maxn]; //g[] 来存值 9 struct node 10 { 11 int num, id; 12 bool operator < (const node& other)const 13 { 14 return num < other.num; //默认从小到大 15 } 16 }t[maxn]; 17 int now = 0; 18 int main() 19 { 20 scanf("%d", &n); 21 for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); 22 for(int i = 1; i <= n; ++i){t[i].num = a[i]; t[i].id = i;} 23 sort(t + 1, t + n + 1); //要离散,先排序 24 for(int i = 1; i <= n; ++i) 25 { 26 if(i == 1 || t[i].num != t[i].num) now++; //如果是和前面不同的数,now++(因为已经排好序了) 27 g[now] = t[i].num; a[t[i].id] = now; //此时的a[i]存的就是原来a[i]是第几小的数 28 } 29 for(int i = 1; i <= n; ++i) printf("%d ", a[i]); 30 printf("\n"); 31 for(int i = 1; i <= n; ++i) printf("%d ", g[i]); 32 printf("\n"); 33 return 0; 34 }
输入:
5
2 100 4 1 4
输出:
2 4 3 1 3
1 2 4 100
此时我们还能发现,指针 now 不仅表示了有 now 个不同的数,也表示了第 now 小的数是什么
稍微改一下,就是例题
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int maxn = 1e5 + 5; 8 int n, a[maxn], g[maxn], tot[maxn]; 9 bool cmp(int a, int b) 10 { 11 return a > b; 12 } 13 struct node 14 { 15 int num, id; 16 bool operator < (const node& other)const 17 { 18 return num < other.num; 19 } 20 }t[maxn]; 21 int now = 0; 22 int main() 23 { 24 scanf("%d", &n); 25 for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); 26 for(int i = 1; i <= n; ++i){t[i].num = a[i]; t[i].id = i;} 27 sort(t + 1, t + n + 1); 28 for(int i = 1; i <= n; ++i) 29 { 30 if(i == 1 || t[i].num != t[i - 1].num) now++; 31 g[now] = t[i].num; a[t[i].id] = now; 32 } 33 for(int i = 1; i <= n; ++i) tot[a[i]]++; 34 sort(tot + 1, tot + n + 1, cmp); 35 printf("%d\n", tot[1]); 36 return 0; 37 }
2.
这种做法是我前最近学到的,不仅想法简单,而且实现起来也很方便。
首先我们将所有数存到一个数组中,然后排个序,再去个重(用unique函数),最后用lower_bound查找每一个数的位置,即离散化后的值。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int maxn = 1e5 + 5; 5 int a[maxn], t[maxn]; 6 int main() 7 { 8 int n; scanf("%d", &n); 9 for(int i = 1; i <= n; ++i) {scanf("%d", &a[i]); t[i] = a[i];} 10 sort(a + 1, a + n + 1); 11 int _n = unique(a + 1, a + n + 1) - a - 1; 12 for(int i = 1; i <= _n; ++i) 13 t[i] = lower_bound(a + 1, a + _n + 1, t[i]) - a; 14 for(int i = 1; i <= n; ++i) printf("%d ", t[i]); 15 return 0; 16 }
输入:
7
1 2 8 4 6 4 100
输出:
1 2 5 3 4 3 100