圆盘覆盖,计算几何(圆盘问题,LA 2572)
2017-01-30 14:19
369 查看
就是自己不会做呀,不知道该如何判断一个圆盘是否被其他圆盘覆盖了,想通过判断交点是否满足某些条件来判断是否覆盖,但就是很难找到一些的简单的规律吧。
然后看大白书做的。
题目说,就算输入数据有+-5e-13的变化,答案仍然不会有变化。①
这句话十分值得注意,它不是在对你作精度要求,而是在告诉你圆盘的可见部分或不可见部分都具有一定的大小。
换句话说,如果我们把eps设得更小一点(这无所谓,只会更准确),比如1e-13,那么离圆心距离r+5e-13的点P1会被判定为在圆外,并且这个点一定在某个我们可以确定的可见部分或者不可见部分里面,不会因为精度问题而刚好在边界上或者跳到了另一个我们不知道的区域里。当然,离圆心距离r-5e-13的点P2也是同样的道理。
那么从上往下看,第一个圈住P1,P2的圆(为什么连P2点也要一起圈住呢?首先由于①,所以这肯定不会导致错误。其次我也不知道这是为什么。。。自己又交了一份只圈住P1的代码,也AC了,只能说自己认为圈住两个的代码应该会更鲁棒。)将会在P1点被看到,所以vis。
一个圆有如此之多的(P1,P2)点对,那么我该选择哪些来用于判断呢?
显然一个区域找一个点就好了,而一个区域一定是由有限段的弧围成的。那么我们不妨每一段弧找一个点对,这样虽然略有重复,但是重复的不多而且不会漏。
至于弧的话必然就是由圆被交点截出来的。
那么解法就出来了。
枚举任意两个圆,求出交点,把交点在圆上的极角保存到这个圆上。那么这些极角所对应的端点就把圆分成了许多段。最后我们针对每一段弧求一个(P1,P2)点对,然后从上往下找第一个圈住这个点对的圆,并把它标记为vis就好了。(显然第二个以及更后面圈住这个点对的圆已经被第一个圆覆盖了,所以不vis)具体代码还有更多细节需要注意就不详说了。
注意如果一个圆不与任何其他圆有交点,那么当且仅当它被在它上面的圆完全覆盖时,他才会看不见。所以枚举在它上面的圆,然后看一下是否包含了它的圆心即可。
(其实如果圆A上面的圆B包含了A的圆心,是有两种情况的,一是A包含于B,二是B包含于A。在二的情况下A是不会被B完全覆盖的,但是在这种情况下,当我们枚举到B时,A会被重新标记为vis的。我是从下往上枚举得哈。这也算是弄拙成巧了。)
好吧,后来又想了想发现包含P1,P2的意思应该是,分别标记包含P1的圆与包含P2的圆,而不是一起包。详见代码2。
代码
代码2
#include<bits/stdc++.h>
using namespace std;
const int maxn = 110;
const double PI = acos(-1);
struct Point
{
double x,y;
Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Point ReadPoint()
{
double x,y;
scanf("%lf %lf",&x,&y);
return Point(x,y);
}
const double eps = 1e-13;
int dcmp(double x)
{
if(fabs(x)<eps) return 0;
return x<0?-1:1;
}
Point operator + (Point A,Vector B)
{
return Point(A.x+B.x,A.y+B.y);
}
Vector operator - (Point A,Point B)
{
return Vector(A.x-B.x,A.y-B.y);
}
double Dot(Point A,Point B)
{
return A.x*B.x+A.y*B.y;
}
double Len(Vector A)
{
return sqrt(Dot(A,A));
}
double Dist(Point A,Point B)
{
return Len(A-B);
}
double Angle(Vector A,Vector B)
{
return acos(Dot(A,B)/Len(A)/Len(B));
}
struct Circle
{
Point c;
double r;
bool vis;
vector<double>rad;
Circle(Point c=Point(),double r=0):c(c),r(r)
{
vis=false;
rad.clear();
}
Point point(double rad)
{
double R=r+5e-13;
return Point(c.x+R*cos(rad),c.y+R*sin(rad));
}
Point point_(double rad)
{
double R=r-5e-13;
return Point(c.x+R*cos(rad),c.y+R*sin(rad));
}
};
Circle ReadCircle()
{
Point P=ReadPoint();
double r;
scanf("%lf",&r);
return Circle(P,r);
}
double angle(Vector A)
{
return atan2(A.y,A.x);
}
void GetCircleCircleInterSection(Circle& C1,Circle& C2)
{
double d=Dist(C1.c,C2.c);
if(dcmp(d)==0) return;
if(dcmp(C1.r+C2.r-d)<0) return;
if(dcmp(fabs(C1.r-C2.r)-d)>0) return;
double ang=angle(C2.c-C1.c);
double rad=acos((C1.r*C1.r+d*d-C2.r*C2.r)/(2*d*C1.r));
C1.rad.push_back(ang+rad);
if(dcmp(rad)!=0) C1.rad.push_back(ang-rad);
ang=angle(C1.c-C2.c);
rad=acos((C2.r*C2.r+d*d-C1.r*C1.r)/(2*d*C2.r));
C2.rad.push_back(ang+rad);
if(dcmp(rad)!=0) C2.rad.push_back(ang-rad);
}
int n;
Circle a[maxn];
void GetVis(Point P1)
{
for(int i=n-1;i>=0;i--)
{
if(dcmp(Dist(P1,a[i].c)-a[i].r)<=0)
{
a[i].vis=1;
break;
}
}
}
int main()
{
//freopen("data.txt","r",stdin);
while(scanf("%d",&n)==1&&n)
{
for(int i=0;i<n;i++)
a[i]=ReadCircle();
for(int i=0;i<n;i++)
for(int j=0;j<i;j++)
GetCircleCircleInterSection(a[i],a[j]);
for(int i=0;i<n;i++)
{
if(a[i].rad.size()==0)
{
GetVis(a[i].point(0));
GetVis(a[i].point_(0));
}
else
{
sort(a[i].rad.begin(),a[i].rad.end());
a[i].rad.push_back(a[i].rad[0]+2*PI);
for(unsigned int j=1;j<a[i].rad.size();j++)
{
GetVis(a[i].point((a[i].rad[j]+a[i].rad[j-1])/2));
GetVis(a[i].point_((a[i].rad[j]+a[i].rad[j-1])/2));
}
}
}
int cnt=0;
for(int i=0;i<n;i++)
if(a[i].vis)
cnt++;
printf("%d\n",cnt);
}
return 0;
}
1988d
然后看大白书做的。
题目说,就算输入数据有+-5e-13的变化,答案仍然不会有变化。①
这句话十分值得注意,它不是在对你作精度要求,而是在告诉你圆盘的可见部分或不可见部分都具有一定的大小。
换句话说,如果我们把eps设得更小一点(这无所谓,只会更准确),比如1e-13,那么离圆心距离r+5e-13的点P1会被判定为在圆外,并且这个点一定在某个我们可以确定的可见部分或者不可见部分里面,不会因为精度问题而刚好在边界上或者跳到了另一个我们不知道的区域里。当然,离圆心距离r-5e-13的点P2也是同样的道理。
那么从上往下看,第一个圈住P1,P2的圆(为什么连P2点也要一起圈住呢?首先由于①,所以这肯定不会导致错误。其次我也不知道这是为什么。。。自己又交了一份只圈住P1的代码,也AC了,只能说自己认为圈住两个的代码应该会更鲁棒。)将会在P1点被看到,所以vis。
一个圆有如此之多的(P1,P2)点对,那么我该选择哪些来用于判断呢?
显然一个区域找一个点就好了,而一个区域一定是由有限段的弧围成的。那么我们不妨每一段弧找一个点对,这样虽然略有重复,但是重复的不多而且不会漏。
至于弧的话必然就是由圆被交点截出来的。
那么解法就出来了。
枚举任意两个圆,求出交点,把交点在圆上的极角保存到这个圆上。那么这些极角所对应的端点就把圆分成了许多段。最后我们针对每一段弧求一个(P1,P2)点对,然后从上往下找第一个圈住这个点对的圆,并把它标记为vis就好了。(显然第二个以及更后面圈住这个点对的圆已经被第一个圆覆盖了,所以不vis)具体代码还有更多细节需要注意就不详说了。
注意如果一个圆不与任何其他圆有交点,那么当且仅当它被在它上面的圆完全覆盖时,他才会看不见。所以枚举在它上面的圆,然后看一下是否包含了它的圆心即可。
(其实如果圆A上面的圆B包含了A的圆心,是有两种情况的,一是A包含于B,二是B包含于A。在二的情况下A是不会被B完全覆盖的,但是在这种情况下,当我们枚举到B时,A会被重新标记为vis的。我是从下往上枚举得哈。这也算是弄拙成巧了。)
好吧,后来又想了想发现包含P1,P2的意思应该是,分别标记包含P1的圆与包含P2的圆,而不是一起包。详见代码2。
代码
#include<bits/stdc++.h> using namespace std; const int maxn = 110; const double PI = acos(-1); struct Point { double x,y; Point(double x=0,double y=0):x(x),y(y){} }; typedef Point Vector; Point ReadPoint() { double x,y; scanf("%lf %lf",&x,&y); return Point(x,y); } const double eps = 1e-13; int dcmp(double x) { if(fabs(x)<eps) return 0; return x<0?-1:1; } Point operator + (Point A,Vector B) { return Point(A.x+B.x,A.y+B.y); } Vector operator - (Point A,Point B) { return Vector(A.x-B.x,A.y-B.y); } double Dot(Point A,Point B) { return A.x*B.x+A.y*B.y; } double Len(Vector A) { return sqrt(Dot(A,A)); } double Dist(Point A,Point B) { return Len(A-B); } double Angle(Vector A,Vector B) { return acos(Dot(A,B)/Len(A)/Len(B)); } struct Circle { Point c; double r; bool vis; vector<double>rad; Circle(Point c=Point(),double r=0):c(c),r(r) { vis=false; rad.clear(); } Point point(double rad) { double R=r+5e-13; return Point(c.x+R*cos(rad),c.y+R*sin(rad)); } Point point_(double rad) { double R=r-5e-13; return Point(c.x+R*cos(rad),c.y+R*sin(rad)); } }; Circle ReadCircle() { Point P=ReadPoint(); double r; scanf("%lf",&r); return Circle(P,r); } double angle(Vector A) { return atan2(A.y,A.x); } void GetCircleCircleInterSection(Circle& C1,Circle& C2) { double d=Dist(C1.c,C2.c); if(dcmp(d)==0) return; if(dcmp(C1.r+C2.r-d)<0) return; if(dcmp(fabs(C1.r-C2.r)-d)>0) return; double ang=angle(C2.c-C1.c); double rad=acos((C1.r*C1.r+d*d-C2.r*C2.r)/(2*d*C1.r)); C1.rad.push_back(ang+rad); if(dcmp(rad)!=0) C1.rad.push_back(ang-rad); ang=angle(C1.c-C2.c); rad=acos((C2.r*C2.r+d*d-C1.r*C1.r)/(2*d*C2.r)); C2.rad.push_back(ang+rad); if(dcmp(rad)!=0) C2.rad.push_back(ang-rad); } int n; Circle a[maxn]; void GetVis(Point P1,Point P2) { for(int i=n-1;i>=0;i--) { if(dcmp(Dist(P1,a[i].c)-a[i].r)<=0&&dcmp(Dist(P2,a[i].c)-a[i].r)<=0) { a[i].vis=1; break; } } } int main() { //freopen("data.txt","r",stdin); while(scanf("%d",&n)==1&&n) { for(int i=0;i<n;i++) a[i]=ReadCircle(); for(int i=0;i<n;i++) for(int j=0;j<i;j++) GetCircleCircleInterSection(a[i],a[j]); for(int i=0;i<n;i++) { if(a[i].rad.size()==0) { GetVis(a[i].point(0),a[i].point_(0)); a[i].vis=1; for(int j=i+1;j<n;j++) if(dcmp(Dist(a[i].c,a[j].c)-a[j].r)<0) { a[i].vis=0; break; } } else { sort(a[i].rad.begin(),a[i].rad.end()); a[i].rad.push_back(a[i].rad[0]+2*PI); for(unsigned int j=1;j<a[i].rad.size();j++) GetVis(a[i].point((a[i].rad[j]+a[i].rad[j-1])/2),a[i].point_((a[i].rad[j]+a[i].rad[j-1])/2)); } } int cnt=0; for(int i=0;i<n;i++) if(a[i].vis) cnt++; printf("%d\n",cnt); } return 0; }
代码2
#include<bits/stdc++.h>
using namespace std;
const int maxn = 110;
const double PI = acos(-1);
struct Point
{
double x,y;
Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Point ReadPoint()
{
double x,y;
scanf("%lf %lf",&x,&y);
return Point(x,y);
}
const double eps = 1e-13;
int dcmp(double x)
{
if(fabs(x)<eps) return 0;
return x<0?-1:1;
}
Point operator + (Point A,Vector B)
{
return Point(A.x+B.x,A.y+B.y);
}
Vector operator - (Point A,Point B)
{
return Vector(A.x-B.x,A.y-B.y);
}
double Dot(Point A,Point B)
{
return A.x*B.x+A.y*B.y;
}
double Len(Vector A)
{
return sqrt(Dot(A,A));
}
double Dist(Point A,Point B)
{
return Len(A-B);
}
double Angle(Vector A,Vector B)
{
return acos(Dot(A,B)/Len(A)/Len(B));
}
struct Circle
{
Point c;
double r;
bool vis;
vector<double>rad;
Circle(Point c=Point(),double r=0):c(c),r(r)
{
vis=false;
rad.clear();
}
Point point(double rad)
{
double R=r+5e-13;
return Point(c.x+R*cos(rad),c.y+R*sin(rad));
}
Point point_(double rad)
{
double R=r-5e-13;
return Point(c.x+R*cos(rad),c.y+R*sin(rad));
}
};
Circle ReadCircle()
{
Point P=ReadPoint();
double r;
scanf("%lf",&r);
return Circle(P,r);
}
double angle(Vector A)
{
return atan2(A.y,A.x);
}
void GetCircleCircleInterSection(Circle& C1,Circle& C2)
{
double d=Dist(C1.c,C2.c);
if(dcmp(d)==0) return;
if(dcmp(C1.r+C2.r-d)<0) return;
if(dcmp(fabs(C1.r-C2.r)-d)>0) return;
double ang=angle(C2.c-C1.c);
double rad=acos((C1.r*C1.r+d*d-C2.r*C2.r)/(2*d*C1.r));
C1.rad.push_back(ang+rad);
if(dcmp(rad)!=0) C1.rad.push_back(ang-rad);
ang=angle(C1.c-C2.c);
rad=acos((C2.r*C2.r+d*d-C1.r*C1.r)/(2*d*C2.r));
C2.rad.push_back(ang+rad);
if(dcmp(rad)!=0) C2.rad.push_back(ang-rad);
}
int n;
Circle a[maxn];
void GetVis(Point P1)
{
for(int i=n-1;i>=0;i--)
{
if(dcmp(Dist(P1,a[i].c)-a[i].r)<=0)
{
a[i].vis=1;
break;
}
}
}
int main()
{
//freopen("data.txt","r",stdin);
while(scanf("%d",&n)==1&&n)
{
for(int i=0;i<n;i++)
a[i]=ReadCircle();
for(int i=0;i<n;i++)
for(int j=0;j<i;j++)
GetCircleCircleInterSection(a[i],a[j]);
for(int i=0;i<n;i++)
{
if(a[i].rad.size()==0)
{
GetVis(a[i].point(0));
GetVis(a[i].point_(0));
}
else
{
sort(a[i].rad.begin(),a[i].rad.end());
a[i].rad.push_back(a[i].rad[0]+2*PI);
for(unsigned int j=1;j<a[i].rad.size();j++)
{
GetVis(a[i].point((a[i].rad[j]+a[i].rad[j-1])/2));
GetVis(a[i].point_((a[i].rad[j]+a[i].rad[j-1])/2));
}
}
}
int cnt=0;
for(int i=0;i<n;i++)
if(a[i].vis)
cnt++;
printf("%d\n",cnt);
}
return 0;
}
1988d
相关文章推荐
- LA 2572 圆盘的相互覆盖问题,圆弧极角排序,中点代替圆弧,轻微扰动的影响判断
- [BZOJ 2731][HNOI 2012]三角形覆盖问题(计算几何+扫描线暴力)
- JOJ 2109 && POJ 1981 Circle and Points 计算几何 单位圆覆盖问题
- 【漂浮法或线段树】 解决矩阵覆盖(计算几何)问题
- 【LA 2572】Viva Confetti, Kanazawa 2002 (计算几何,圆)
- Viva Confetti(几何+圆盘覆盖问题)
- 多边形重心问题-计算几何
- HDU 4606 Occupy Cities (计算几何+最短路+二分+最小路径覆盖)
- 【计算几何】信号覆盖
- NYOJ-3 多边形重心问题【计算几何】
- 计算几何中的精度问题 (转)
- 计算几何专项:LA 2453
- HDU 3007 Buried memory(计算几何の最小圆覆盖,模版题)
- 计算几何中的精度问题(会不断更新)
- HDU4606 Occupy Cities 计算几何+最小路径覆盖
- 计算几何与圆心和球有关的计算问题
- hdu 4606 Occupy Cities - 计算几何 + 最短路 + 最小路径覆盖
- ☆【计算几何】信号覆盖
- 计算几何中的精度问题(转)
- 【转】计算几何中的精度问题