[常用技巧] 数据离散化入门介绍

离散化是程序设计中一个非常常用的技巧,它可以有效地降低时间复杂度。其基本思想就是在众多可能的情况中“只考虑我需要用到的值”。下面用几个例子说明,如何运用离散化改进一个低效的,甚至根本不可能实现的算法。

先看UVA10173
[ http://acm.uva.es/p/v101/10173.html ]

题目意思很简单,给定平面上n个点的坐标,求能够覆盖这些点的最小的矩形面积。

[常用技巧] 数据离散化入门介绍_第1张图片

由于不知道这个矩形到底有倾斜多少度,所以先从倾斜0度的矩形开始考虑:要使覆盖这些点的矩形面积最小,那么这个矩形的每一条边上必定都存在至少一个点。转换成代码就是:对于所有的点坐标进行查找,选出其中x最小,y最小,x最大,y最大的值,所围成的矩形就是当前倾角为0°时的最小面积。

然而这只考虑当倾角为0°时的情况,想要求出所有可能的情况,当然是很自然地想到,枚举所有矩形可能的倾角,对于每一个倾角,都能计算出最小的矩形面积,最后取其中的最小值。

这个算法是否是正确的呢?且不说是否正确,它从理论上甚至根本不可能实现。矩形的倾角是一个实数,它有无数种可能,你永远不可能枚举出每一种可能的情况。也就是说,这个倾角是一个连续的变量,“连续”自然是无法枚举倾角的根本原因。我们需要一种方法,把这个连续的变量变成一个一个的值,也就是“离散”的变量。这个过程就是我们所说的离散化。

经过思考可以证明,最小面积的矩形不但要求四条边上都有一个点,而且还要求有一条边上,有两个或者两个以上的点,这点可以用反证法来证明。假设每条边上都只有一个点,那么我们可以将整个矩形旋转一定的角度,从而使两个(或以上)的点落在同一条边上,得到一个面积更小的矩形。

于是我们发现,矩形内部某一条边的斜率必定与某两点之间的连线相同。如果我们计算出了所有过两点的直线的倾角,那么矩形倾角的取值只有可能是这些倾角,或者它减去90度以后的倾角。于是,这个倾角就被我们离散化了。

再看另外一个问题:
给定二维平面上的n个矩形,求其覆盖的总面积。平时的想法就是开一个与二维坐标规模相当的二维bool数组G[i][j] 代表坐标(i,j)到(i+1,j+1)这个1*1的格子是否被覆盖。可惜这个想法在这里有一些问题,因为这个题目中坐标范围相当大(10^8),然而矩形的数量n<=100远远小于坐标范围。每个矩形会在横坐标,纵坐标上各使用两个值,也就是说,100个句型的坐标也不过用了最多200个值,实际能代表划分范围的值就是这些,其他的都可以算作是冗余数据。
于是我们考虑将这些值作为新的坐标值重新划分整个平面,省去中间的若干没有出现的坐标值没有影响。把坐标范围离散化到1~200之间的数,于是一个200*200的二维数组就足够了。
实现方法就是将所有矩形“排序后处理”,对横坐标(纵坐标)进行一次排序并映射为1到2n的整数,同时再开一个新的200*200数组,记录新坐标的每两个相邻坐标之间在离散化前实际的距离是多少。

如下图,给定8个点:
(1,2)
(1,7)
(7,2)
(7,7)
(3,1)
(3,5)
(10,2)
(10,5)
仔细观察,是怎样把这8个点构造成先前的二位数组,以及离散化后的两个数组的。
[常用技巧] 数据离散化入门介绍_第2张图片

先扫描一遍x轴上的区间,即可以分成多少段,每段的长度为多少。扫描到的坐标按序排列为(0),1,3,7,10,
所以可以归纳成3段,每段的横向长度分别为1,2,4,3。
再扫描y轴上的区间,按序排列为(0),1,2,5,7,同样归纳成4段,每段的纵向长度分别为1,1,3,2。
所以离散化后的数组为
1,2,4,3
1,2,4,3
3,6,12,9
2,4,8,6

离散化思想的核心,就是把无限的可能性中归纳到有限的空间中去,以此降低算法的时间复杂度和空间复杂度。

你可能感兴趣的:(0,常用技巧,————ACM训练————)