您的位置:首页 > 编程语言

光影切割问题

2015-12-29 16:17 344 查看

一、问题:

   不少人很爱玩游戏,例如 CS 。 游戏设计也成为程序开发的热点之一,我们假设要设计破旧仓库之类的场景作为战争游戏的背景。仓库的地面会因为阳光从屋顶的漏洞或者窗口照射进来而形成许多光照区域和阴影区域。为了简单起见,假设不同区域的边界都是直线 , 我们把这些直线都叫做“光影线”,并且不存在三条光影线相交于一点的情况。 

     那么,如果我们需要快速计算某个时刻,在 X 坐标[ A, B] 区间的地板上被光影划分成多少块。如何才能写出算法来计算呢? 
分析:
个人认为这是一个很能考验人的观察能力,在乱中找出规律,无奈我到最后还是看了书上的解法之后,继续感叹作者的算法之巧妙!不过自己也在不断的进步吧,总有一天也能达到这样的水平。
在分析问题之前,我们可以先研究一下图形中不同线段之间的关系。
在下图中,原来是有两条线,此时两条线把区域分为了四个部分,那么我们如果再加一条线呢?





此时,我们把书上的分析直接拿过来看有一下:


这个需要你好好的看一下上面的那两个图,我也是看了好久,才明白如果有N条直线,M个交点,那么区域的数目为N+M+1这个结论的。
好好理解下这句话,自己在纸上画图看看:如果增加m个交点那么,那么这条直线就被分为m+1段,每段就会把原来的一块区域一分为二,也就是增加了m+1个区域。

三、解法一

好了,既然我们找到了区域的数目和直线的数目和交点个数的关系了,那么接下来我们直接求出二者不就行了,这个就是解法一。
我们只要求出所有直线的两两相交的点,然后查找哪些点在[A , B]之间,进而就可以求出平面被划分的块数。我们可以考虑将N条直线的所有的交点存储于数组 Intersect中,然后进行计算。这样的话,我们的原始问题就转化为查找交点数组的问题了。
那么我们需要初始化这个存储交点的数组了,初始化的时候需要求出每条直线和其他N-1条直线的交点,那么此时初始化的过程的时间复杂度就是O(N的2次幂)。
然后我们就去查找每个交点是否落在[A ,B]区域内,这个只需要判断交点的X轴坐标是否在[A,B]之间就行,这个耗费了O(N)的复杂度。
最后注意我们去注意在初始化数组的时候我们得到的点是重复的,假设直线a 和 直线 b有一个交点,那么当我们查找a的时候,我们就会发现它和b有一个直线,当我们查找
b的时候就会发现a和其有一个交点,那么就是说我们的交点每一个都重复了一次,那么我们把其直接除以2就行。
最后我们得出所有的交点都在[A , B]之间的数目是 (初始化的数组的长度L)/2    再减去不在区间[A ,B] 之间的点数目m。
直线的数目我们已经知道是N个,这N个直线一定都落在[A,B]之间,因为题目中要求了,直线不能平行于Y轴。
注:我不太理解书中说的,查询的时间复杂度的问题。

四、解法二

哎,最让人感叹的就是解法二,哎,问题转化能力啊,思考,思考。
我们先看下面这张图

不知道你发现什么没有?
看左边这张图你发现,当直线a和直线b有交点的时候是在什么时候,在左边a和墙的交点是在b和墙的交点的上面的,而在右边正好相反,b和墙的交点排在了a的上面,原因很简单。
再看右边这张图,同样的,原来的和墙的交点的排序是a,b,c,因为这三个直线都两辆相交,所以到了右边就变成了c,b,a。
发现什么规律没有,其实交点的数目和c,b,a的逆序数是相等的,不信你可以在纸上画一下加入a,b,c三条直线有两个交点的话,在右边的排序的逆序数是不是2.
那么我们就可以把这个事情转化为求逆序数的问题了。
当然最暴力的方式就是用最原始的方法求得逆序数了,就是从第一个开始查询,往后查询,发现小于这个数的就记下来,依次查询下去,把所有的和加起来就是逆序数。
当然这种求的逆序数的时间复杂度是O(N的2次幂)和解法一没什么好的改变。那么还有一种方法就是采用分治的策略来求得逆序数。
什么是分治的策略,其实就是采用了归并排序的思想,举个例子加入说一个数列的顺序是8,7,9,6,10,1,3
假如我们采用二路归并,那么刚开始先把8和7分为一组,那么其逆序数为1,原因是我们在进行排序的时候把二者的位置进行了一次的调换。
同理9和6的逆序数也是1,10和1的逆序数为1,然后就剩下3为一组,其逆序数为0,。
接下来进行第二轮,7,8,6,9,
首先需要把这两组在上一轮的逆序数相加,我们先去比较7和6发现需要排序的时候,我们需要把后面一组的数排在前面的时候,我们去看一下前面的那个一组还剩下多少个数,然后我们就把逆序数加上这么多的数,依次下来。
分治策略采用了归并排序的思想,所以其时间复杂度为O(N*log以2为底N)。




五、总结

我们最后把书上的总结写出来,原因是我觉得说的真是太好了。从上面的分析中可以看出,在把一个相当复杂的解答转化为一个相对容易的解答的过程中没精力了三次问题转换和转化,在实际问题的分析中,我们要尽量考虑,问题是否就这么复杂,是否可以进一步转化呢?是否有更加简单的或者优雅的办法来实现呢?
然而我认为这正是我在解决问题时从来没有考虑过的,或者说这种能力正式我所缺少的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 编程之美