题目链接:https://vjudge.net/problem/UVA-10969
题目大意:
给出多个圆,按顺序输入,下层圆会被后来的上层的圆覆盖住,问最后从上往下看,能看到的圆弧的长度
题目解析:
从最下层的圆开始,求出该圆与其上层圆的所有交点,然后对这些交点从小到大进行极角排序
从最小的点开始,与其后一个相邻的点为一组,求出以这两点划分出的弧的中点,然后将这个中点与上层所有的圆进行比较,看是否能被圆包含,如果能被包含,说明这段弧是被圆覆盖住的,不可见(确实是这样)。将所有可见弧的长度相加,即为所求
这道题做的时候想的是用交点划分出的弧作为区间,然后让区间进行叠加,最后求出总的区间的对应的角度,有点偏重模拟了,写起来十分繁琐复杂
计算几何做起来还是画画图更清晰,一开始没想通为什么是中点被圆包含就算整段弧都被圆覆盖,后来画个图一看就想通了
AC代码:
#include<iostream>
#include<stdio.h>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
int n,m;
const double PI=acos(-1.0);
const double eps=1e-6;
const double TWO_PI = 2.0 * PI;
//double radius[1000];
double NormalizeAngle(double ang){ //防止角度大于2PI
return ang - TWO_PI*floor(ang/TWO_PI);
}
struct Point{
double x,y;
Point(double x=0,double y=0):x(x),y(y){}
};
double radius[1000];
typedef Point Vector;
Point P[1000];
int sgn(double d){
if(fabs(d)<eps){//等于零
return 0;
}
if(d<0){ //大于零
return -1;
}
return 1; //小于零
}
double Dot(const Point& A, const Point& B)
{ return A.x*B.x + A.y*B.y; }
double Length(const Vector& A)
{ return sqrt(Dot(A, A)); }
double Cross(Vector A,Vector B){
return A.x*B.y-A.y*B.x;
}
double getAngle(Vector A,Vector B){//得到两向量弧度制下的夹角
return fabs(atan2(A.y,A.x)-atan2(B.y,B.x));
}
double Angle(const Vector& A){
return atan2(A.y, A.x);
}//获得单个向量的角度
bool cmp1(Vector A,Vector B)
{
if(atan2(A.y,A.x)!=atan2(B.y,B.x))
return atan2(A.y,A.x)<atan2(B.y,B.x);
else return A.x<B.x;
}
bool operator == (const Point& A, const Point& B)
{ return sgn(A.x - B.x) == 0 && sgn(A.y - B.y) == 0; }
Point operator - (const Point& A, const Point& B)
{ return Point(A.x - B.x, A.y - B.y); }
void getCircleIntersection(const Point &c1,double r1,const Point &c2,double r2,vector<double> &rad){
double d=Length(c1-c2);
if(sgn(d)==0) return;
if(sgn(d-r1-r2)>0) return;
if(sgn(d-fabs(r1-r2))<0) return;
double base=Angle(c2-c1);
double ang=acos((r1*r1+d*d-r2*r2)/(2.0*r1*d));
rad.push_back(NormalizeAngle(base+ang));
rad.push_back(NormalizeAngle(base-ang));
}
bool isVisible(const Point &C,int id){
for(int i=id+1;i<n;i++){
double d=Length(C-P[i]);
if(sgn(d-radius[i])<=0){
return 0;
}
}
return 1;
}
int main() {
cin>>m;
for(int i=1;i<=m;i++){
cin>>n;
for(int j=0;j<n;j++){
cin>>radius[j]>>P[j].x>>P[j].y;
}
double ans=0.0;
for(int j=0;j<n;j++){
vector<double> rad;
rad.push_back(0.0);
rad.push_back(TWO_PI);
for(int k=0;k<n;k++){
if(k==j){
continue;
}
getCircleIntersection
(P[j],radius[j],P[k],radius[k],rad);
}
sort(rad.begin(),rad.end());
for(int k=0;k<rad.size()-1;k++){
double mid=(rad[k]+rad[k+1])/2;
double ang=(rad[k+1]-rad[k]);
Point C(P[j].x+radius[j]*cos(mid),
P[j].y+radius[j]*sin(mid));
if(isVisible(C,j)){
ans+=radius[j]*ang;
}
}
}
printf("%.3f\n",ans);
}
return 0;
}
/*
3
1
10 0 0
2
5 0 0
10 0 0
2
1 0 0
1 1 0
*/