离散化【学习笔记】

引入

小丁:小智,你不觉得我们小区旁边的树木太多太挤了吗?
小智:确实。要不我们把一些树移走?小区对面的学校旁可正缺树呢!
小丁:不过我们又不能自己把树移走,得找人帮忙。
小智:嗯。要不我们就在树旁边标记一下,让园林工人移植一下吧。
小丁和小智开始了自己的活儿……

小丁从左往右,每数 120 棵便标记一棵树。
小智从左往右,每数 422 棵便标记一棵树。

小智:我们最好算算需要移走多少棵树,好让园林工人校对。
小丁:我怎么知道小区旁一共有多少棵树!

如果 story 中的树木棵树达到了 96000000 棵,不能直接数组计数,该怎么办?

概念

离散:用一个小数据(通常是为了数组计数)表示大数据
离散化:将一组数据离散(通常是其大数据在数组中的大小排名,大数据排名必须和小数据排名相同)
为什么要学离散化:你要的数字可能会是负数,也可能会超级大


首先,我们可以定义 sign 数组,sign[x]=y 表示小数据 x 表示大数据 y。

如一个数组a={3,57294,365,2854024857,-2532},将其离散化:
用 1 表示 -2532
用 2 表示 3
用 3 表示 365
用 4 表示 57294
用 5 表示 2854024857

那么离散后的数组a={2,4,3,5,1},sign数组为 {-2532,3,365,57294,2854024857}

我们随时可以调用原来的数据 (只是外面多加了 sign[])

sign[ a[1] ]=3 sign[ a[2] ]=57294

我们很容易得到伪代码:

输入 n;
for(i=1...n)
{
    输入 a[i];
    sign[i]=a[i];
}
sort(将sign数组从小到大排序);
unique(把sign数组中重复的number去掉);
for(i=1...n)
{
    a[i]=find a[i] in sign;//找 a[i] 的位置(a[i]在a数组中的大小排名)
}
题目时间

Luogu洛谷 P1496 火烧赤壁

这是一道最基础的离散化题目。我们令 c[i] 表示第 i 位置是否有起火看,其中的 i 是离散化之后的数据(原来的 i 可能是负数,也可能有点大)。每当输入 Ai 和 Bi,sign数组记录一下 Ai 和 Bi(后期离散),之后对 sign 排序、去重。然后再从离散后的 Ai~Bi 标记即可。

AC 代码

#include
#include
using namespace std;
int n,a[20005],b[20005],c[400005],sign[40005],m,ans;
//离散化,sign[i]=x表示数字 i 代表原数字 x 
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i]>>b[i];
		sign[++m]=a[i];
		sign[++m]=b[i];
		//记录Ai和Bi,进行离散化
	}
	sort(sign+1,sign+1+m);
	unique(sign+1,sign+1+m);
	for(int i=1;i<=n;i++)
	{
		a[i]=lower_bound(sign+1,sign+1+m,a[i])-sign;//二分查找a[i]在sign中的位置
		b[i]=lower_bound(sign+1,sign+1+m,b[i])-sign;//同理
		c[a[i]]++;//差分记录
		c[b[i]]--;
	}
	int cnt=0;
	for(int i=1;i<=m;i++)
	{
		if(cnt)ans+=sign[i]-sign[i-1];//有火,用离散前的数据差加答案
		if(c[i])cnt+=c[i];//差分计算
	}
	cout<<ans;
	return 0;
}

你可能感兴趣的:(c++,算法)