E. Fruit Slicer--计算几何+两圆公切线
程序员文章站
2022-04-01 20:43:00
...
https://open.kattis.com/problems/fruitslicer
很明显,直线是两圆公切线的时候,经过的圆最多,而n只有100,公切线最多4条,只需要(n^2)暴力枚举两圆公切线,然后再(n)得到公切线经过圆的数目,最后取max即可。复杂度O(n^3)级别
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const double eps = 1e-6;
const double PI = acos(-1.0);
int sgn(double x){
if(fabs(x) < eps)return 0;
if(x < 0)return-1;
else return 1;
}
double sqr(double x){return x*x;}
struct Point{
double x, y;
Point(){}
Point(double _x,double _y){
x = _x;
y = _y;
}
bool operator == (Point b)const{
return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;
}
bool operator < (Point b)const{
return sgn(x-b.x)== 0-sgn(y-b.y)?0:x<b.x;
}
Point operator-(const Point &b)const{
return Point(x-b.x,y-b.y);
}
//叉积
double operator ^(const Point &b)const{
return x*b.y-y*b.x;
}
//点积
double operator *(const Point &b)const{
return x*b.x + y*b.y;
}
//返回长度
double len(){
return hypot(x,y);//库函数
}
//返回长度的平方
double len2(){
return x*x + y*y;
}
//返回两点的距离
double distance(Point p){
return hypot(x-p.x,y-p.y);
}
Point operator +(const Point &b)const{
return Point(x+b.x,y+b.y);
}
Point operator *(const double &k)const{
return Point(x*k,y*k);
}
Point operator /(const double &k)const{
return Point(x/k,y/k);
}
};
struct Line{
Point s,e;
Line(){}
Line(Point _s,Point _e){
s = _s;
e = _e;
}
//求线段长度
double length(){
return s.distance(e);
}
double dispointtoline(Point p){
return fabs((p-s)^(e-s))/length();
}
};
struct Circle{
Point p;
double r;
Circle(){}
Circle(Point _p,double _r){
p = _p;
r = _r;
}
// 过一点知道角度和距离 求另一点
Point GP(double b) {
return Point(p.x + cos(b)*r, p.y+sin(b)*r);
}
int relationline(Line v){
double dst = v.dispointtoline(p);
if(sgn(dst-r) < 0)return 2;
else if(sgn(dst-r) == 0)return 1;
return 0;
}
}CC[200],C1,C2;;
const double Pi=acos(-1.0);
struct Intersect{
Point P[2];
bool operator <(const Intersect &rtm) const{
return sgn(P[0].x-rtm.P[0].x)<0||(sgn(P[0].x-rtm.P[0].x)==0&&sgn(P[0].y-rtm.P[0].y)<0);
}
}ANS[7];
typedef Point Vector;
double GPA(Vector A){//Get_Polar_Angle
return atan2(A.y,A.x);
}
double GL(Vector A){//Get_Length
return sqrt(A*A);
}
//求公切线,这个模板都没问题
int GCCI(Circle A,Circle B){//Get_Circle_Circle_Intersection
int cnt=0,a=0,b=1;
if(A.r<B.r) swap(A,B),swap(a,b);
Vector u=B.p-A.p;
double d=GL(u),rdec=A.r-B.r,radd=A.r+B.r;
if(sgn(d-rdec)<0) return 0; //内含
if(sgn(d)==0&&A.r==B.r) {
ANS[1].P[a] = A.p;
ANS[1].P[b] = A.GP(PI);
return 1; //重合,无线多,原本返回-1,但这题特殊
}
double base=GPA(u);
if(sgn(d-rdec)==0){ //内切
ANS[++cnt].P[a]=A.GP(base); ANS[cnt].P[b]=B.GP(base);
return cnt;
}
double da=acos((A.r-B.r)/d); //2条外公切线
ANS[++cnt].P[a]=A.GP(base+da); ANS[cnt].P[b]=B.GP(base+da);
ANS[++cnt].P[a]=A.GP(base-da); ANS[cnt].P[b]=B.GP(base-da);
if(sgn(d-radd)==0){ //1条内公切线
ANS[++cnt].P[a]=A.GP(base); ANS[cnt].P[b]=B.GP(base+Pi);
}
else if(sgn(d-radd)>0){ //2条内公切线
da=acos((A.r+B.r)/d);
ANS[++cnt].P[a]=A.GP(base+da); ANS[cnt].P[b]=B.GP(base+da+Pi);
ANS[++cnt].P[a]=A.GP(base-da); ANS[cnt].P[b]=B.GP(base-da+Pi);
}
return cnt;
}
int main(){
int cnt;
int n;
int aans=0;
int last_ans=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%lf %lf", &CC[i].p.x, &CC[i].p.y);
CC[i].r=1;
}
bool flag=true;
{
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
C1=CC[i];C2=CC[j];
cnt=GCCI(C1,C2);
if(cnt<=0) continue;
sort(ANS+1,ANS+cnt+1);
for(int i=1;i<=cnt;i++)
{//ans.p切点
aans=0;
Line temp = Line(ANS[i].P[0],ANS[i].P[1]);
int res;
Point xx,yy;
for(int k=0;k<n;k++)
{
res=CC[k].relationline(temp);
if(res)aans++;
}
last_ans=max(last_ans,aans);
}
}
}
if(n == 1) last_ans = 1;
cout<<last_ans<<endl;
}
return 0;
}
下一篇: 线段树应用——扫描线