中山大学校队选拔赛第一章题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也要学会模仿(注意严格证明)。

时间: 2024-10-26 20:53:18

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

中山大学校队选拔赛第一章题1【计算生成树】------2015年1月23日

1.1问题描述 1.2问题分析 本题主要考查图论中生成树及组合数学的求法.通过观察我们可以发现当输入为n时,我们一共有(5*n-n)=4n个点.通过思考我们可以知道,要想求得生成树,我们必须使所有五角形的圈全部破掉.那么我们可以思考: 如果对于一个五角形而言,它的每一条边都不删除,那么我们可以发现这必定不能构成生成树,因为这会导致在五角形内任意两个点会至少含有两条路径,不符合生成树的含义.所以我们可以得出如下结论: (1)对于有边数为n的回圈,我们可以发现一共有4n个点,共有5n条边.根据生成树

中山大学校队选拔赛第一章题4【简单数迷Simple Kakuro】-------2015年1月28日

一:题意描述 本题就是给定一个迷宫,其中第一行和第一列都给定了数值.现在我们的任务就是需要把剩余的空格用1-9的数字把它填满,并且每行每列数值之和需要和行列标定的值相等.问最后是否可行,如果有多种方案需要输出一种方案. 二 :题目分析 本题主要考查DFS当中剪枝技巧的利用以及DFS的方向规划问题. 首先我们可以根据题目已知信息得出行之和必须等于列之和.(第一次剪枝) 然后我们可以根据每一个数字不能在同一行或者同一列重复出现[设定标记]进行剪枝(第二次剪枝) 对于如果某一行(一列)数值还没有填满但

中山大学选拔赛第一章题1【计算生成树】------2015年1月23日

1.1问题描述 1.2问题分析 本题主要考查图论中生成树及组合数学的求法.通过观察我们可以发现当输入为n时,我们一共有(5*n-n)=4n个点.通过思考我们可以知道,要想求得生成树,我们必须使所有五角形的圈全部破掉.那么我们可以思考: 如果对于一个五角形而言,它的每一条边都不删除,那么我们可以发现这必定不能构成生成树,因为这会导致在五角形内任意两个点会至少含有两条路径,不符合生成树的含义.所以我们可以得出如下结论: (1)对于有边数为n的回圈,我们可以发现一共有4n个点,共有5n条边.根据生成树

我的Python成长之路---第一天---Python基础(1)---2015年12月26日(雾霾)

2015年12月26日是个特别的日子,我的Python成之路迈出第一步.见到了心目中的Python大神(Alex),也认识到了新的志向相投的伙伴,非常开心. 尽管之前看过一些Python的视频.书,算是有一点基础.但在这里我要保持空杯心态,一切从头开始.好了不多说,Let's Python!!!! 一.Python简介 Python是著名的“龟叔”Guido van Rossum在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言.目前Python已经成为实际上除了中国最流行的开发语

中山大学校队选拔赛第二试题试题3【Compressed suffix array】-------2015年2月8日

一:题目大意 本题通过给定三个数组S0,P,S,其中S0是1到2n的一个排列,P具有2n个整数,且满足: 数组S是把数组S0中所有奇数元素全部删除并将所有偶数元素除以2并按照原来的相对顺序进行排列而得. 现在给定数组S和数组P,我们需要反求数组S0. 二:题目分析 我们通过对数组P的递推式分析可知:当数组S0的元素是偶数时,这个元素所对应的Pi的值一定等于i.当数组S0的元素为奇数时,我们可以知道对应的Pi一定不等于i.那么我们可以先对数组P扫一遍,把存在Pi=i的元素全部填好,然后再对数组P重

中山大学校队内部选拔赛试题试题2【New Year Gift】--------2015年2月8日

一 题意分析 本题就是说给定n种类型的珍珠,每一种珍珠都有固定的数目.现在我们需要串需要M种不同类型珠子的珍珠链,现在问我们最多可以组成多少珍珠链? 二 题目分析 这道题主要考查二分思想的运用.对于二分法我们首先需要找到上界与下界,这里的下界我们可以设置为0,上界可以设定为最大(2000000表示一个珍珠链只需要一个珍珠),我们在进行二分,如何二分我们可以定义一个变量sum,不同类型珍珠数量的数组num[],中间变量Mid.对于每一种类型的珍珠,如果num[i]>mid则让sum+=mid,否则

我的Python成长之路---第一天---Python基础(2)---2015年12月26日(雾霾)

三.数据类型 Python基本类型(能够直接处理的数据类型有以下几种)主要有5种 1.整数(int) Python可以处理任意大小的整数,当然包括负整数,在程序中的表示方法和数学上的写法一模一样,例如:1,100,-8080,0,等等. 2.浮点数(float) 浮点数也就是数学中的小数.由于整数和浮点数在计算机内部存储的方式是不同的,整数运算永远是精确的,而浮点数运算则可能会有四舍五入的误差.需要注意. 3.字符串 字符串是以单引号'或双引号"括起来的任意文本,比如'abc',"xy

我的Python成长之路---第一天---Python基础(6)---2015年12月26日(雾霾)

七.列表——list Python的列表是一种内置的数据类型,是由Python的基本数据类型组成的有序的集合.有点类似C语言的数组,但与数组不同的是,Python在定义列表的时候不用指定列表的容积(长度),可根据需要任意扩展,另外列表的内的元素可以是不同的数据类型,当然既然是任何数据类型,当然也包括另一个列表也就是嵌套.Python中列表使用中括号[]括起来,例如[1,2,True,'ABC',[5,'678']]. 1.列表的切片 通字符串一样列表也支持切片操作,例如我们有一个列表A_list

我的Python成长之路---第一天---Python基础(4)---2015年12月26日(雾霾)

五.数据运算与数据运算符 算术运算符 算术运算符 运算符 描述 示例 + 加法 >>> 14 - 59 - 减法 >>> 14 - 5 9  *  乘法 >>> 5 * 14 70  / 除法 >>> 14 / 5 2.8  %  取模,即两个的数商的余数部分 >>> 14 % 5 4  **  幂运算 >>> 2 ** 3 8  //  整除,返回两个数的商的整数部分 >>> 1