接下来n行分别输入三个整数圆心坐标x、y和半径r。
(1≤x、y≤1000,1≤r≤100)
输出:
输出一个整数,表示闭合区域的个数。
初步分析:
虽然题目中已经对圆的位置有了一定的约束,但实际上可能的情况还是很多的,我们希望能找到一种简单而通用的算法。
由于数据都是整数,我们的初步想法是这道题的精度要求不高,因此可以采用一种基于floodfill的算法:
在平面内每隔一定距离取一个点形成点阵,删除所有在圆内的点,然后在剩下的点中floodfill求连通块,最后减1就是答案。
算法复杂度为O(k2n),k为1000长度中取点的个数。
图3
我们知道,这个算法的正确性或精度取决于k的大小,而对于这道题目的时限来说,这个算法远远不能达到要求,因此我们需要改进算法。
第一步离散化:
考虑点集的每一横行,我们发现每一个圆在其上覆盖的点一定是连续的。
图4
于是我们想到在每一横行中不必枚举每一个点,转而计算每一个圆在其上的覆盖区间,这一步可以用数学算法在常数时间内算出。
然后合并相交的区间。
在预处理时,可以将圆按横坐标排好序,这样在合并区间时会变得更简单。
最后根据被覆盖的区间求出未被圆覆盖的区间。
仿照方法1,我们将所有的区间记录下来,并在相邻两行有公共部分的区间连一条边,然后求连通块数,最后减1即为答案。
由于区间个数太多,无论DFS还是BFS都消耗了太多内存,因此我们可以利用图的特殊性,创造一种效率较高的顺序扫描求连通块数法:
1.初始化第一横行(仅有一个区间),这一区间从属于1个集合。
2.给下一横行的每一区间初始化一个新的集合仅包含他。
3.如果两行中某两个区间有交集,合并他们所从属的集合。
4.如果某一集合中的所有区间在下一行都没有继承者,答案加1。
5.重复2~4,直到扫描至最后一行。
可以看到,这个算法的复杂度为O(nk),在时限内可以得到较高的精度。
但是,由于题目中存在某些精度要求非常高的数据,并且此算法本身的精度不够,最终这个算法得到了WrongAnswer。
所以,我们仍需要进一步改进。
第二步离散化:
仔细观察程序,我们发现,似乎有很多无用的工作。
如下例:
图5
在绿色区域中,我们直观的感觉到,这肯定是同一片区域,因为在其中根本就没有其他的圆,而我们的程序却在其中辛苦的一步步跟踪,直到最后确认最下一行确实是最上一行的后继。
于是我们有了另一种想法——省略多余的操作。
我们发现,区间之间的继承关系在大多数时候都没有改变,只在仅有的少数几个时刻才会发生改变:
●一个圆新出现时,将一个区间分成两半。
●两圆相交,一个区间逐渐减小并被吞噬。
●两圆相交,一个新的区间生成。
●一个圆的最下端,两个区间合并为一个区间。
这样,我们仅需记录所有的点事件,并模拟区间的生长消亡,然后求解。
注意到,我们没有必要记录区间的确切长度。
但由于点事件的定性有四种,其中中间两种较难分辨,程序很容易出错。
事实上,我们甚至没有必要记录点事件的性质,对于每一个点事件,取它之前的极短一段时间和之后的极短一段时间,然后利用方法2中的顺序扫描法,下一个点事件的前驱直接继承这个点事件的后继。
至此,由于采用了离散化法,本题已被完美解决。
时间复杂度为O(n3)。
例二、Empirestrikesback
题目大意:
Saddam的国度为半径为R的圆,圆心在(0,0),其中有N个化学武器工厂(坐标分别为(Xi,Yi)),GeorgeII将在每个工厂中投掷炸弹(炸弹将炸平以工厂为圆心一定半径内的一切)。
但由于不清楚Saddam躲藏在哪里,GeorgeII决定使炸弹的杀伤范围将整个国土都覆盖,问题是炸弹的最小杀伤半径为多少。
输入:
第一行包含两个数N(1≤N≤300),R(1≤R≤1000)分别表示炸药数和半径。
接下来n行每行包含一个炸药的描述。
输入两个数(Xi,Yi)(Xi2+Yi2≤R2)
输出:
最小的半径,保留5位小数
初步想法:
题目仅仅求一个数,所以我们考虑使用二分法求得答案。
二分法步骤如下:
1.初始化
。
2.求出中间数
。
3.判断以Mid为半径是否能覆盖整个圆,如果能则
,否则
。
4.当
时退出,否则重复2~3。
二分法将一个最优性问题转化为一个可行性问题,现在问题的重点是判断当半径为确定值时,大圆是否被完全覆盖。
算法分析:
初看起来,这题和上一题非常相似。
但如果沿用上一例的算法,时间上完全承受不了。
细细分析,才发现两题之间有些许不同,上一题要求出区域个数,而本题仅需判定是否完全覆盖,由计数问题转化为判定性问题,而我们仍在用原先的思路解题,必然无法得到高效的算法。
因此我们需要重新设计算法
考虑最原始的思路:
枚举大圆内每个点,判定其是否被小圆覆盖。
如果每一个大圆内的点都被小圆覆盖,则大圆一定被小圆完全覆盖。
但是,由于题目对精度的要求非常高,因此本算法必然不能正确求得答案,我们想到可以利用离散化方法优化此算法。
第一步离散化:
沿用上例的思路,我们将一个横行看成一个整体。
对于每一横行求得每个小圆覆盖的区间,然后合并所有相交的区间,如果最后仅剩一个区间,且这个区间包含了大圆在其上覆盖的区间,则可以判定这个大圆在这条横线已被完全覆盖。
如果对于所有横线大圆都被完全覆盖,则我们可以近似的认为大圆已被小圆完全覆盖。
由于题目仅仅需要我们判定是否完全覆盖,因此取特殊点是一个不错的想法。
考虑每一横行中关键点的左右两端极其接近的点判断其是否被其他圆覆盖。
图6
可以看到,如果仅看我们所取的所有特殊点,那么这些特殊点所组成的图形恰好为每个圆向左偏移一点和向右偏移一点的图形之和。
并且,其中在原先的圆内的一部分可以不考虑。
又因为偏移量非常小,因此我们可以将所有特殊点视为在偏移前的圆的边界上。
第二步离散化
应用集中处理的原则,我们同时处理同一圆上的所有点。
我们看到,每个圆在另一个圆的边界上覆盖的区间一定是连续的。
因此,问题转化为很多个小区间是否完全覆盖整个区间。
这和例1中第一步离散化后得到的问题十分类似,因此可以使用类似的算法解决。
图7
复杂度分析:
二分的复杂度O(logR);
对于区间的排序O(NlogN);
区间合并O(N);
枚举N个圆O(N);
算法总复杂度:
O(N2logNlogR)
至此,问题已被完美解决。
一些小的优化:
事实上,如果在排序时,按照区间的中点的极角进行排序,则区间的序和半径长度无关,可以在预处理时完成,复杂度可降为O(N2(logN+logR))。
由于每一个圆被覆盖所需要的最小半径都是相互独立的,所以我们可以认为它是一个常数,而我们所需要的是求出这些常数中的最大值。
所以可以在枚举每个圆的时候,先判断一下它在当前最小半径下是否能被完全覆盖,如果能覆盖则不再二分。
杨弋同学和袁昕颢同学证明了这种算法的算法复杂度期望值为O(N2*logN+N*logN*logR),证明如下:
考虑一种算法:
1.随机选一个小圆
2.二分测出完全覆盖它的所需的最小半径
3.然后和其他所有的小圆比较
4.把不能完全覆盖的小圆留下来
5.递归
可以发现,这个算法和上文所述算法本质上是相同的。
而这个算法很容易证明其复杂度为O(N+logN*logR),所以原算法的复杂度为O(N2*logN+N*logN*logR)。
另外,可以通过随机化避免最坏情形发生。
【总结】
离散化法在计算几何中是一种比较普遍适用的算法。
当问题与圆有关时,离散化法能利用区域的离散化将问题转化为相对简单的“图形”计算几何问题。
离散化法的本质在于化“零”为“整”,化“曲”为“直”,实现简化算法复杂度的目的。
当然,由于这一过程不可避免地破坏了曲线的性质,因此离散化法作为一种较为朴素的处理方法,往往不如其他直接利用曲线性质的算法高效。
但其普遍适用的特性和简洁性使得它能够在计算几何算法中占有一席之地。
本文虽然分析的是圆的计算几何问题,但很容易推广到其他曲线的计算几何问题。
【感谢】
感谢安徽师大附中的杨弋同学和长沙市长郡中学的袁昕颢对例二算法复杂度的证明。
感谢所有帮助过我的人。
【参考文献】
刘汝佳,黄亮《算法艺术与信息学竞赛》清华大学出版社
【附件】
例一《DolphinPool》一题的源程序
求两圆交点(基于余弦定理,有一定误差):
intget_inter(intx1,inty1,intx2,inty2,doublerad1,doublerad2,double&ret1,double&ret2)
{
doubleb=sqrt((double)(sqr(y2-y1)+sqr(x2-x1)));
if(b>rad1+rad2||b{
ret1=0;
ret2=0;
return0;
}
elseif(b{
ret1=0;
ret2=2*pi;
return0;
}
//以上为判断特殊情况
doublemid=atan2(y2-y1,x2-x1);
doublea=rad1,c=rad2;
doublet=acos((sqr(a)+sqr(b)-sqr(c))/(2*a*b));
//所取三角形的三顶点分别是两圆圆心和其中一交点
ret1=mid-t;
ret2=mid+t;
//返回值为交点在第一个圆上的极角
return0;
}
【附录】
[例一原题]
DolphinPool
TimeLimit:
1000MS MemoryLimit:
10000K
Description
InanewlyconstructeddolphinpoolintheKishislandinPersianGulf,oneofthefungamesisasfollows:
thegamedirectorthrowsseveralplasticringsinthepoolsuchthatcenterofnoringliesinsideanyotherring,andnotworingsaretangent.Thedolphinsaretrainedtojumpoutonthedirector'swhistlethroughtheclosedareasthatarecompletelyoutsidetherings,onedolphinfromonesucharea.Thedolphinsjumpoutifandonlyifthenumberofclosedareasexactlyequalstothenumberofdolphins.
Youaretowriteaprogramtogiventhefollowinginput/outputdescription,findsthenumberofclosedareasbetweenringstohelpthedolphinsdecidetojumpoutornot.
Input
Thefirstlineincludesthenumberoftestcases(atmost20).EachtestcasedatahasanintegerN(1<=N<=20),thenumberofplasticrings,initsfirstline.FollowingthefirstlinethereareNlines,eachcontainingthreeintegers,thefirstandsecondbeingthexandycoordinatesofthecircleofthering,andthethirdisitsradius.Coordinatesarepositiveintegerslessthan1000andtheradiusisintherange1...100.
Output
Foreachtestcase,theremustbeonelineintheoutputincludingthenumberofclosedareasinthattestcase.
SampleInput
2
4
10010020
10013520
13510020
13513520
1
101040
SampleOutput
1
0
Source
Tehran2000
[例二原题]
Empirestrikesback
TimeLimit:
1.0second
MemoryLimit:
16MB
Background
ManyyearshavepassedsincethegoodandwiseemperorGeorgeIItheGreatbegantoruletheculturalandcivilizedEmpire.Oh,theworldhecreatedissomightyandbeautiful!
Underhisrulemajesticcitiesofmarbleandsteelarebeingturnedintothesky,andhugefieldsarebeingscatteringwithseeds.Thechildrenplay,theoldmenlaugh,whiletheworkpeopleandthepeasantsforgethecommonweal...
ButonceGeorgegottoknow,thatfearfuldangerthreatenedthemankind.MaliciousandcrueldictatorSaddamIIItheTerrible,whoruledmuchlessculturalandcivilizedRepublic,intendstocreatethenewestchemicalweaponandseizethepowerovertheplanet.
Problem
Accordingtoasecretservicereport,SaddamconstructedNchemicalweaponfactorieswithintheRepublicfrontier,whichisacircleofradiusRwithitscenteratthepoint(0,0).Eachfactoryisskillfullydisguisedasahospital,aschooloranoldpeople'shomeandlocatedatthepointwithcartesiancoordinates(Xi,Yi).
Saddam'svileintentionsdidnotpleaseGeorgeatall.Sohedecidedtodestroyallthefactoriesbybombing.Allbombsshouldhavethesameeffectivecasualtyradiusandbedroppedpreciselyontothecorrespondingfactory.
Eachbombtransformsanyobjectwithinitseffectivecasualtyradiusintoascorchinggascloud.ThisveryfactpromptedGeorgetoafunnythought,thatitwouldbegreattokilltwobirdswithonestoneandtransformSaddamhimselfintosuchcloud.Unfortunately,thesecretservicefailedtodefineexactwhereaboutsofthevillain.ThatiswhyGeorgewantstocalculatetheeffectivecasualtyradiusofthebombssothat,beingdroppedpreciselyontothefactories,theywoulddestroySaddamregardlessofhislocationwithintheRepublic.Bytheway,itneedsalotofveryexpensivepolonium-210tocreateahigh-powerbomb,sotheeffectivecasualtyradiusshouldbeminimal.
Input
ThefirstlinecontainstheintegernumbersN(1≤N≤300)andR(1≤R≤1000).EachofthenextNlinescontainstheintegernumbersXiandYi(Xi2+Yi2≤R2)forthecorrespondingfactory.
Output
Youshouldoutputthedesiredeffectivecasualtyradius.Theradiusshouldbeprintedwithatleastfivedigitsafterdecimalpoint.
SampleInput
44
02
0-2
20
-20
SampleOutput
2.94725152