对于离散化一开始听了感觉很高大上,可是学了发现也不会太难。
首先我们讲讲为什么要用离散化?
对于有一些数据结构例如:并查集。就要以数字编号作为下标,可如果题目数字给的很大呢?
凉凉。
对于这种题目都会有个特点,数字很大,可数量不多,那么我们就可以重新对它进行编号,这个过程其实就是离散化。
对于离散化我们比较重要的就是映射方程,也就是根据什么对它进行编号,比较常用而且好用的就是根据数量进行编号。
所以我们主要就是讲怎么根据数量进行编号。
例如:55、22、31、46、22,这一串数字我们对他进行离散化。
我们先对所有的数进行排序,例如:22、22、31、46、55。为什么要排序?
这个问题我们后面再讲,和后面一个函数有关。
然后我们还要去去重,这个很麻烦(如果手写),还好C++的发明者给了一个很良心的函数,unique这个是去重函数,头文件是algorithm,unique(l,r)表示对从l到r这个地址(注意是地址!!)进行去重,其实这个函数并不是删除重复的数字,而是把没重复的数字调到前面来,重复的数字就到后面去了,而且这个函数是有返回值的,它返回的是非重复的数字的最后一个数字的地址,也就是:22、31、46、55、22,返回的是第四个数55的地址。
然而这个函数有个bug就是重复的数字必须相邻,这就是我们要排序的原因。
这样去重完,我们就可以根据他们的顺序进行重新编号(就是离散化)了(根据操作完后的顺序进行编号,第一个数字就是一号,就是上面的22)。然而我们有发现了一个问题,怎么才可以知道一个数到底是在第几个呢?
这个还有一个很良心的函数lower_bound,lower_bound(l,r,a),这个函数的意思就是在l到r这个地址范围内,从l到r第一个大于等于a的数。返回的就是这个数的地址。而这个函数内部实现其实就是二分查找,时间复杂度是O(log n)的。
这样这个问题不就解决了吗?(剩下的地址处理会在代码中讲解)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100100],t[100100];//a为我们存数字的数组,t为我们操作的数组
bool cmp(int x,int y)
{
return x<y;
}
int main()
{
int n,m;
scanf("%d",&n);//n表示总共要处理多少个数
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),t[i]=a[i];//输入n个数,并且赋值一遍到t数组
sort(t+1,t+1+n,cmp);//排一下序,从小到大
m=unique(t+1,t+1+n)-t-1;//algorithm这个库里的函数, 这个是去重函数,意思是把不重复的数排在前面,一定要排序,这个要求相同的数在一起
//unique(l,r)表示对l到r这个区间进行去重操作,注意l和r是地址,并且返回补充复的数列的最后一个数的地址,对于地址的处理我们只需要减去数组开头的地址就好了,也就是t
//为什么还有-1操作,因为我是从1开始存的啊
//例如:1 3 3 4 4 5,操作后是:1 3 4 5 3 4,返回的是第四个数的地址,注意是地址
for(int i=1;i<=n;i++)//对于每个数计算
printf("%d : %d \n",a[i],lower_bound(t+1,t+1+m,a[i])-t);//iostream这个库里的函数,意思是二分查找第一个比所给定数大于或者等于的数的地址
//lower_bound(l,r,a)表示从l开始找到r返回第一个比a大或者等于的数的地址(二分的时间复杂度) 注意l和r是地址
//这边没有-1的原因是因为我没有把0算进去
return 0;
}
上个例题练练手吧。
好题
前置技能: 并查集
如果不会并查集的话就可以关闭网页了,或者先去看一下再来练练手。
讲解:
这一题其实也不难,对于每个等式我们只需要在他们中连一条边,后面遇到不等于就直接看一下两个点是否连通,是连通的话就GG了,否则就OK。判连通?不就是并查集嘛。
不过这个数字范围很坑,所以我们要离散化一下。
注意点:
1、数组要两倍。
2、初始化要两倍。
3、对于所有操作要排序,先把合并的操作处理,在判断。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct node
{
int x,y,temp;
}AC[100100];//结构体存一下
int f[200100],a[200100];
bool cmp(int x,int y)//排序
{
return x<y;
}
bool cmp1(const node x,const node y)//因为1>0所以从大到小绝对可以保证先合并
{
return x.temp>y.temp;
}
int get(int x)//查询尝龟操作
{
if(x==f[x])
return x;
f[x]=get(f[x]);
return f[x];
}
int main()
{
int T;
scanf("%d",&T);//多组数据
while(T--)
{
int n,bj=1,m,tot=0;
memset(a,0,sizeof(a));//初始化
memset(f,0,sizeof(f));
memset(AC,0,sizeof(AC));
scanf("%d",&n);
for(int i=1;i<=200000;i++)//注意两倍
f[i]=i;
for(int i=1;i<=n;i++)
{
scanf("%d %d %d",&AC[i].x,&AC[i].y,&AC[i].temp);//读入
a[++tot]=AC[i].x;//把所有要离散化的数字存起来
a[++tot]=AC[i].y;
}
sort(a+1,a+1+tot,cmp);//排序
sort(AC+1,AC+1+n,cmp1);//排序
m=unique(a+1,a+1+tot)-a-1;//求出有多少个没重复的数字
for(int i=1;i<=n;i++)
{
if(AC[i].temp==1)//合并
{
int f1=get(lower_bound(a+1,a+1+m,AC[i].x)-a),f2=get(lower_bound(a+1,a+1+m,AC[i].y)-a);//找到离散化后
if(f1!=f2)//如果没联通
f[f1]=f2;//连一下
}
else
{
int f1=get(lower_bound(a+1,a+1+m,AC[i].x)-a),f2=get(lower_bound(a+1,a+1+m,AC[i].y)-a);//找到离散化后
if(f1==f2)//有连通的话
bj=0;//GG
}
}
if(bj)//看一下是否GG
printf("YES\n");
else printf("NO\n");
}
return 0;
}
以上就是离散化的内容,如果有什么不清楚的可以留言。