离散化解析

对于离散化一开始听了感觉很高大上,可是学了发现也不会太难。
首先我们讲讲为什么要用离散化?
对于有一些数据结构例如:并查集。就要以数字编号作为下标,可如果题目数字给的很大呢?
凉凉。
对于这种题目都会有个特点,数字很大,可数量不多,那么我们就可以重新对它进行编号,这个过程其实就是离散化。
对于离散化我们比较重要的就是映射方程,也就是根据什么对它进行编号,比较常用而且好用的就是根据数量进行编号。
所以我们主要就是讲怎么根据数量进行编号。
例如: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
#include
#include
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
#include
#include
#include
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;
}

以上就是离散化的内容,如果有什么不清楚的可以留言。

你可能感兴趣的:(数据结构)