离散化思想详解


前言

当有效数据量可控,但数值落在的数值空间非常大的时候,离散化思想就可以派上用场了。


一、引入例题

题目描述

  • 桌面上放了N个平行于坐标轴的矩形,这N个矩形可能有互相覆盖的部分,求它们组成的图形的面积。

输入格式

  • 输入第一行为一个数N (1≤N≤100),表示矩形的数量。下面N行,每行四个整数,分别表示每个矩形的左下角和右上角的坐标,坐标范围为 − 1 0 4 -10^4 104 1 0 4 10^4 104 之间的整数。

输出格式

  • 输出只有一行,一个整数,表示图形的面积。

分析

  每输入一个矩形的时候,在这个矩形的位置填充 true,最后再相加。这个时候不加优化的 暴力 最坏的情况下这个时候空间复杂度大概为 O ( 4 × 1 0 8 ) O(4\times10^8) O(4×108),这还是可以接受的,代码如下: 这里很好理解我就不解释啦QAQ
零优化暴力

#include<cstdio>
#include<algorithm>
using namespace std;
bool map[1001][1001];
int n,x1,y1,x2,y2,xx,yy,ans;
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++) {
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		if(x1>x2)swap(x1,x2);
		if(y1>y2)swap(y1,y2);
		xx=max(xx,x2);
		yy=max(yy,y2);
		for(int x=x1;x<=x2;x++)
			for(int y=y1;y<=y2;y++)
				map[x][y]=1;
	}
	for(int x=0;x<=xx;x++)
		for(int y=0;y<=yy;y++)
			ans=ans+map[x][y];
	printf("%d",ans);
	return 0;
}

但是如果题目将坐标范围扩大到 − 1 0 8 -10^8 108 1 0 8 10^8 108 的话时间和空间都会爆掉, 暴力不可解

优化

时间

  从时间的角度来优化的话,我们可以通过批量的填充来节约时间,也就是说将整个大的矩形转化成小的矩形:(虽然实际情况可能并不是这样)
分割
若每次这样填充,在最后遍历的时候,时间效率会大大提高。

空间

  从空间角度来看,我们可以直接储存每个小块,再储存他们的面积:
优化后
此时所需要的空间由原来的 7 × 10 7\times10 7×10 变成了 2 × 4 × 4 2\times4\times4 2×4×4哇,你真了不起
这里附上代码:

//矩形覆盖,离散化处理 
#include<cstdio>
#include<algorithm>
#define nn 1001
using namespace std;
struct aty{
	int x1,x2,y1,y2;
} a[nn];
struct bty{
	long long  N,xy;
}b[2*nn],c[2*nn];
bool cmp(bty a,bty b){ return a.xy<b.xy;}
long long xx[nn],yy[nn],x1,y1,x2,y2,nowx,nowy,ans; 
bool map[201][201];
int n,newx,newy;
int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
		b[i].N=b[n+i].N=c[i].N=c[i+n].N=i;
		b[i].xy=x1;b[i+n].xy=x2;
		c[i].xy=y1;c[i+n].xy=y2;		
	}
	sort(b+0,b+2*n,cmp);
	sort(c+0,c+2*n,cmp);	// 排序完后用下标代替左右x、y的值 
	for(int i=0;i<2*n;i++){
		if(nowx!=b[i].xy) nowx=b[i].xy,newx++,xx[newx]=nowx;
		if(a[b[i].N].x1==0) a[b[i].N].x1=newx;
		else a[b[i].N].x2=newx;		
		if(nowy!=c[i].xy) nowy=c[i].xy,newy++,yy[newy]=nowy;
		if(a[c[i].N].y1==0) a[c[i].N].y1=newy;
		else a[c[i].N].y2=newy;
	}
	for(int i=0;i<n;i++){
		for(int x=a[i].x1+1;x<=a[i].x2;x++)
			for(int y=a[i].y1+1;y<=a[i].y2;y++)
				map[x][y]=1;
	}
	for(int x=2;x<=newx;x++)
		for(int y=2;y<=newy;y++)
			if(map[x][y]==1)
				ans+=(xx[x]-xx[x-1])*(yy[y]-yy[y-1]);
	printf("%d",ans);
	return 0;
}

优化总结

经过时间和空间的优化,这道题的时间成本就可以降到可以接受的程度,而这种优化思想,就叫做离散化思想

二、离散化思想总结

概述

  • 离散化,就是把无限空间中有限的个体映射到有限的空间中去,以提高算法的时空效率。
  • 很多算法的复杂度与数据中的最大值有关,比如用数组实现的一对一标记。时常会遇到这种情况:数据的范围非常大或者其中含有负数但数据本身的个数并不是很多(远小于数据范围)。在这种情况下,如果每个数据元素的具体值并不重要重要的是他们之间的大小关系的话,我们可以先对这些数据进行离散化,使数据中的最大值尽可能小且保证所有数据都是正数

预期功能

  离散化的原理和实现都很简单。为了确保不出错且尽可能地提高效率,我们希望离散化能实现以下几种功能:
1.保证离散化后的数据非负且尽可能的小
2.离散化后各数据项之间的大小关系不变,原本相等的也要保持相等。
由此,找出数据项在原序列中从小到大排第几就是离散化的关键

思路

可以通过下面的方法以O(nlogn)的时间复杂度完成离散化,n为序列长度。
1.对原序列进行排序,使其按升序排列。
2.去掉序列中重复的元素。
3.此时序列中各位置的值和位置的序号就是离散化的映射方式。

例题

这里有一个小小的题目很好的体现了离散化思想:

  • 例如:对于序列105,35,35,79,-7,排序并去重后变为-7,35,79,105,由此就得到了对应关系-7->1, 35->2, 79->3, 105->4。
int n, a[maxn], t[maxn];
//这里以下标1为序列的起点,一般情况下从0开始也可以
for(int i = 1;i <= n;i++){
    scanf("%d", &a[i]);
    t[i] = a[i];//t是一个临时数组,用来得到离散化的映射关系
}
//下面使用了STL中的sort(排序),unique(去重),lower_bound(查找)函数
sort(t + 1, t + n + 1);//排序
int m = unique(t + 1, t + 1 + n) - t - 1;//去重,并获得去重后的长度m
for(int i = 1;i <= n;i++){
    a[i] = lower_bound(t + 1, t + 1 + m, a[i]) - t;//通过二分查找,快速地把元素和映射对应起来
}

总结

  实际上这非常好理解QAQ,所以…我先溜啦,芜湖~。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值