文章目录
- 数据的离散化
- 例题:电影
- 完整代码
数据的离散化
离散化是指将一个无穷大的集合中的若干个元素映射到一个有限的集合中,以便于对那个无穷大的集合进行操作。
在很多情况下:对于一个规定在Z范围内的整数范围,他有可能包含非常多的重复的元素,真正不同的元素仅有M个,因此我们考虑把这Z个元素进行映射到只包含M个元素的集合中,即与 【1-M】个整数建立映射关系。因此如果一个算法的时间空间复杂度与Z有关,则会降低到与M有关,对于某些重复元素非常多的集合,将会大大提高算法的复杂度。
那么显而易见:离散化的首要目的便是给包含Z个元素的集合去重,使得它不包含任意的重复元素,每个元素只出现一次,然后映射到另一个无任何重复元素的集合中,这个集合只包含M个元素。
- 我们把a集合中的某一个元素a[i]映射到b[j]中,b[j]中不含任意重复的元素,相当于是排序+去重后得a集合(可以使用STL的unique实现这个功能)
- 我们想要得知 j 这个序号表示的是哪一个a集合中的元素,即表示哪一个a[i],只需要返回b[j]即可
- 我们想要得知 a[i]这个元素,在b集合中被哪一个序号i所表示,我们就可以使用二分查找来找到它的位置。
int a[11] = { 0,1,1,2,3,4,5,6,9,2,7 }; //注意下标从1开始,a[0]是凑数的
int b[11], m = 0;
void discrete()
{
//排序,去重,实现离散化
sort(a+1,a+1+10);
for (int i=1;i<=n;i++)
{
if (i==1 || a[i]!=a[i-1])
{
b[++m]=a[i];
}
}
}
int find(int x)
{
return lower_bound(b+1,b+1+m,x)-b;
}
举例:
int main()
{
discrete();
cout << b[8] << endl; //查询i=8(1<=i<=m)代替的是哪一个数值:直接返回b[i]
cout << query(5) << endl; //查询a[j]的值被哪个i替代,只需要二分查找a[j]即可
return 0;
}
例题:电影
AcWing 103 电影
这是一道利用了离散化数据的例题。
可以看到此题中关于语言这一个量的限制达到了10^9 ,并且对于这个语言,我们貌似还必须要用一个数组存储起来,否则就无法操作,因此我们的数组就会存储一个非常大的集合,并且我们的数组还不一定能装下,因此我们考虑使用离散化。
做法:
- 首先收集所有的语言:使用ori数组。
- 然后对于ori数组进行离散化处理,保存离散化数据到uni中。
- 根据离散化的数据集合得到每一位科学家懂得的语言编号,保存在ans数组中。
- 遍历所有电影,对于每一个电影,分别求出能够使科学家达到很开心的科学家的人数,其次求出达到比较开心的人数,然后对于这两个数据和以前的值取一个最大的值,记录电影编号。
首先收集所有语言:全部存在ori数组里,数组的下标使用tot记录
cin >> n; //n:n位科学家
for (int i = 1; i <= n; i++)
{
//科学家懂得的语言编号
cin >> p[i];
ori[++tot] = p[i]; //原始数据全部存在ori中
}
cin >> m; //m:m个电影
for (int i = 1; i <= m; i++)
{
//每一部电影的语音语言
cin >> y[i];
ori[++tot] = y[i];
}
for (int i = 1; i <= m; i++)
{
//每一部电影的字幕语言
cin >> z[i];
ori[++tot] = z[i];
}
然后进行离散化数据!!!!!!!!!!
把离散化数据存放在uni数组中
//排序
sort(ori + 1, ori + 1 + tot);
//离散化数据
for (int i = 1; i <= tot; i++)
{
if (i == 1 || ori[i] != ori[i - 1])
{
uni[++k] = ori[i];
}
}
注意:离散化的数据集合uni中只包含了所有的语言编号,并不包含任何其他数据,如人数等等,统计各个人数是在下一步进行的
统计懂得每种语言的科学家分别有多少人???
使用find进行查询:给出p[i]表示的是未离散化的大集合,要找到这个p[i]在离散化后被哪个编号i所映射,使用find函数。 得到的是一个映射标号i,然后ans[i]表示的是懂得第i种语言的人数。
for (int i = 1; i <= n; i++)
{
//查询懂得某种语言的人数有多少
ans[find(p[i])]++;
}
统计电影的语音和字幕的人数
同理:
- ans[find(y[i])]:表示懂得第i部电影的语音的人数,因为在上一步我们已经预处理了懂得每种语言的人数,所有说把一个语言编号i传递给ans,得到的就是懂得 i语言的人数。
- ans[find(x[i])]:懂得第i部电影的字幕的人数。
统计完成后,如果这一部电影的resy(懂得语音的人数)比上一部电影的resy多(注意:上一部电影的语音用res1保存,上一部的字幕用res2保存),那么更新电影编号;
如果相等,那么再比较字幕。。。
for (int i = 1; i <= m; i++)
{
resy = ans[find(y[i])];//懂得语音的人数
resz = ans[find(z[i])];//懂得字幕的人数
if (resy > res1 || (resy == res1 && resz > res2))
{
res = i; //记录电影编号
res1 = resy;//记录这部电影的懂得语音的人数
res2 = resz;//记录这部电影的懂得字幕的人数
}
}
最后答案有可能会出现 没有一个人懂得某部电影的语音和字幕,则随便输出一个。
完整代码
!!!!!!!!!! 注意理解这些变量的名字,不要弄混了!!!!!!!!!!!
#include <bits/stdc++.h>
using namespace std;
/*
请你帮忙选择一部电影,可以让观影很开心的人最多。
如果有多部电影满足条件,则在这些电影中挑选观影比较开心的人最多的那一部。
*/
const int N = 200005;
int n, m, resy, resz, res, res1, res2, k = 0, tot = 0;
int p[N], y[N], z[N], ans[3 * N];
int ori[N * 3], uni[3 * N]; //ori存放原始数据,uni存放离散数据 多给点空间
int find(int x)
{
//根据a[j]查询替代此a[j]值的序号j
return lower_bound(uni + 1, uni + 1 + k, x) - uni;
}
int main()
{
cin >> n; //n:n位科学家
for (int i = 1; i <= n; i++)
{
//科学家懂得的语言编号
cin >> p[i];
ori[++tot] = p[i]; //原始数据全部存在ori中
}
cin >> m; //m:m个电影
for (int i = 1; i <= m; i++)
{
//每一部电影的语音语言
cin >> y[i];
ori[++tot] = y[i];
}
for (int i = 1; i <= m; i++)
{
//每一部电影的字幕语言
cin >> z[i];
ori[++tot] = z[i];
}
//排序
sort(ori + 1, ori + 1 + tot);
//离散化数据
for (int i = 1; i <= tot; i++)
{
if (i == 1 || ori[i] != ori[i - 1])
{
uni[++k] = ori[i];
}
}
for (int i = 1; i <= n; i++)
{
//查询懂得某种语言的人数有多少
ans[find(p[i])]++;
}
for (int i = 1; i <= m; i++)
{
resy = ans[find(y[i])];//懂得语音的人数
resz = ans[find(z[i])];//懂得字幕的人数
if (resy > res1 || (resy == res1 && resz > res2))
{
res = i; //记录电影编号
res1 = resy;//记录这部电影的懂得语音的人数
res2 = resz;//记录这部电影的懂得字幕的人数
}
}
if (res == 0)
{
cout << 1;
}
else
{
cout << res;
}
return 0;
}
参考:
《算法竞赛进阶指南》
AcWing 103 题解