离散化总结

122 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情


离散化是一个在不改变一串数字相对大小关系的前提下,将比较大的数字转化为比较小的数字的技巧。有时我们并不需要知道各数字确切的大小,我们只需要保存下来它们之间的大小关系即可,这时候就需要用到离散化。例如给出数列:955500005 78000457 60006060 989989887,经离散化后得到:3 2 1 4。

通过stl实现的离散化模板:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
 
const int N = 1e5+5;
vector<int> all;
 
int find(int x)
{
	return lower_bound(all.begin(), all.end(), x)-all.begin()+1; 
}
 
signed main()
{
	int a[N], n;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
	{
		scanf("%d", &a[i]);
		all.push_back(a[i]);
	} 
	sort(all.begin(), all.end());
	all.erase(unique(all.begin(), all.end()), all.end());
}
 

每次需要用到某个a[i]时只需要调用find(a[i])函数,返回的就是a[i]离散化后的值。另外find函数中最后面的+1代表离散化后最小的数是1,可以根据题目灵活更改。

通过数组实现的离散化模板:

#include <iostream>
#include <cstdio>
#include <algorithm>
#define num first
#define pos second
#define pii pair<int, int>
using namespace std;
//比stl速度要快 
const int N = 1e5+5;
 
signed main()
{
	pii a[N];
	int n, mp[N];
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
	{
		scanf("%d", &a[i].num);
		a[i].pos = i;
	}
	sort(a+1, a+n+1);//默认按num值升序 
	int cnt = 1;
	mp[a[1].pos] = 1;
	for(int i = 2; i <= n; i++)
	{
		if(a[i].num == a[i-1].num)
			mp[a[i].pos] = cnt; 
		else
			mp[a[i].pos] = ++cnt;
	}
}
 

每次需要用到a[i]时只需要替换为mp[i]即可,mp[i]中保存的就是a[i]离散化后的数值。

另外需要特别注意的是区间离散化:即给出某些区间,这些区间端点可能比较大,但我们只关心各区间端点之间的大小关系。

这时需要把区间端点都离散化一遍,这样处理后各区间的相对位置不发生变化,但是可能导致某些区间相连。例如有三个区间:[1, 10], [1, 4], [6, 10],显然[1, 4]与[6, 10]是不相连的,但离散化后三个区间分别为[1, 4], [1, 2], [3, 4],此时后两个区间就连起来了,对于某些题目而言可能是会产生影响的。比如Mayor's posters OpenJ_Bailian - 2528(虽然没有注意到这点也可以AC)。

解决方法也很简单,用stl离散化则只需最后再扫描一遍vector,如果存在两点间距离大于2的两相邻点则在其中再加入一个点,以表示这两点实际上是分隔开的,对于相邻的点之间则不应加入点,表示这两点实际上就是相连的。