首页 > 代码库 > 中山大学校队选拔赛第一章题1【紧急逃离Emergent escape】----2015年1月26日

中山大学校队选拔赛第一章题1【紧急逃离Emergent escape】----2015年1月26日

一: 题意描述

 技术分享

技术分享

   技术分享

    技术分享

二:题目分析

     本题的大致意思是讲:在给定的一个大圆上挖去很多圆(这些圆有的在大圆里面,有的在大圆外面,有的与圆相加),凡是被圆占据的部分则不能通行。

现在给定两个点,(lifeship和controlling room)如果两者能够到达的话表示能够Escape,否则就只有Die hard。

     本题的主要考查图论知识和计算几何方面的知识。首先我们对于这个问题需要建模。我们首先可以把这个大圆看成单独的一个区域。现在的问题就是在整个大圆内找不到一条线可以让lifeship和controlling room 相连。下面我们看看示意图:

技术分享

现在的任务就是在区域A中找一个与区域A(虚线以左)圆弧相交的圆R1,在区域B中与圆弧相交的圆R2.然后通过其它的圆把这两个圆连接起来,此时则一定不能Escape!。现在的任务就是如何把各个陨石(圆)之间的关系表示出来?我们可以这么做:首先我们通过如下条件进行建模:把每一个陨石看做一个点,不同陨石如果相交则连一条边,因而可以建立一个图。(具体建图规则如下)

(1)如果两个圆一个在A区域而另外一个在B区域,并且两个圆的交点在大圆外,我们不考虑两者的关系(即两个点之间不连边);

(2)如果两个圆之间的交点在大圆内,那么我们可以把两个点之间连一条边);

(3)如果两个圆相离,同样也不连边。

通过这样我们便可以建立一个图。剩下的工作就是看能否找到一条从A区域到B区域的连线。下面的工作我们可以采取DFS的方法进行:

(1)首先对各个点进行初始化(利用数组b[]打标记:

        (1.1)如果在大圆外那么我们标记b[i]=0;

        (1.2)如果陨石完全被包含在大圆内,我们打上标记b[i]=3;

        (1.3)如果陨石与大圆相交在区域A内,我们打上标记b[i]=1;

        (1.4)如果陨石和大圆相交在区域B中,我们打上标记b[i]=2;

 (2)下面的工作就是建图,规则如上介绍的进行。

(3)首先找到一个在区域A的点,接下来找与A相邻的点,进一步DFS直到找到在区域B中的点为止结束递归。整个过程可以设置一个标记ok,并初始化为1.

   注意在DFS过程中我们需要学会剪枝。我在代码里面会就如何剪枝给出证明过程。

三 :AC代码

#include<iostream>#include<cmath>#include<string.h>using namespace std;const int maxn=1000+10;const double eps=1e-8;const double pi=acos(-1.0);double d1,d2,R;double xd1,yd1,xd2,yd2;int n;double x[maxn],y[maxn],r[maxn];int a[maxn][maxn];int b[maxn];int cas;double dis(double x,double y){    return sqrt(x*x+y*y);}double helen(double a ,double b,double c)//海伦公式需要牢记{    double p=(a+b+c)/2.0;    return sqrt(p*(p-a)*(p-b)*(p-c));} int intersect(int i,int j )//这是判断两圆是否相交的函数,注意为何可以这样做? {     double di=dis(x[i],y[i]);     double dj=dis(x[j],y[j]);     double ij=dis(x[i]-x[j],y[i]-y[j]);     double S=helen(di,dj,ij);     double Si=helen(di,r[i],R);     double Sj=helen(dj,r[j],R);     double Sij=helen(ij,r[j],r[i]);     return  Si+Sj+Sij>S; } int dfs(int root) {     if(b[root]==2)  return 0;     b[root]=0;//这里需要好好理解为何要这样处理。相当于剪枝     int i;     for(int i=0;i<n;i++) if(b[i]&&a[root][i]==cas)     {         if(dfs(i)==0)   return 0;//说明找到了     }     return 1; } int main() {     int T;     cin>>T;     int ok;     int i,j;     memset(a,0,sizeof(a));     cas=0;     while(T--)     {         cas++;         cin>>R>>d1>>d2;         if(d1>d2)         {             swap(d1,d2);         }         d1=pi/180.0*d1;         d2=pi/180.0*d2;         xd1=R*cos(d1);//这几个函数是如何在圆内利用弧度求坐标!!!         yd1=R*sin(d2);         xd2=R*cos(d2);         yd2=R*sin(d2);         cin>>n;         for( int i=0;i<n;i++)            cin>>x[i]>>y[i]>>r[i];         ok=1;         memset(b,0,sizeof(b));         for(int i=0;i<n;i++)         {            int  d=dis(x[i],y[i]);             if(d>R+eps+r[i])//整个陨石在大圆外             {                 b[i]=0;                 continue;             }             if(r[i]+d+eps<R)//整个陨石在大圆内             {                b[i]=3;                continue;             }             if(dis(x[i]-xd1,y[i]-yd1)<=eps+r[i]||dis(x[i]-xd2,y[i]-yd2)<=eps+r[i])             {                 ok=0;//陨石直接会把lifeship或者controlling room 直接摧毁,结束                 continue;             }           int   di=acos(x[i]/d);             if(y[i]<0.0) di=pi+pi-di;             if(di>d1&&di<d2) b[i]=1;//判断是在A区域还是在B区域             else b[i]=2;         }         if(!ok)         {             cout<<"Die hard"<<endl;             continue;         }         for(int i =0;i<n;i++)         if(b[i])            for(int j =i+1;j<n;j++)            if(b[j])         {             if(dis(x[i]-x[j],y[i]-y[j])<=r[i]+r[j])             {                if(b[i]+b[j]!=3||intersect(i,j))//这里的b[i]+b[j]!=3表示分别在A区域和B区域但是交点在大圆外                    a[i][j]=a[j][i]=cas;             }         }         for(int i=0;i<n;i++)         {              if(b[i]==1) ok=dfs(i);              if(!ok) break;         }           if(ok) cout<<"Escape"<<endl;           else cout<<"Die hard"<<endl;     }     return 0; }

下面我给出DFS中在可以把b[root]置为0的证明过程:

情况1:

技术分享

假设我们首先找到A区域的点C,root点如图所示,如果从root点不能到达B区域的E,那么我们可以确定得出点D从root点照样不能到达B区域的点E,所以当判断不成立的就可以设置为0达到剪枝的作用。

情况2:

技术分享

我们此时可以得知如果递归过程中从C经root不能达到B区域,那么我们可以确定从D经root照样不能到达B区域,此时照样可以剪枝。

四:本题总结

(1)本题建模思想需要好好体会;

(2)本题在计算几何里面涉及到数据处理问题,需要特别注意!

(3)本题在弧度和角度转化,弧度求坐标,海伦公式等数学知识涉及很多,需要好好消化;

(4)本题在DFS时涉及到剪枝问题,以后自己在写DFS也要学会模仿(注意严格证明)。

 

中山大学校队选拔赛第一章题1【紧急逃离Emergent escape】----2015年1月26日