挑战程序设计竞赛 3.6 与平面和空间打交道的计算几何

POJ 1981:Circle and Points

/*
    题目大意:给出平面上一些点,问一个半径为1的圆最多可以覆盖几个点
    题解:我们对于每个点画半径为1的圆,那么在两圆交弧上的点所画的圆,一定可以覆盖这两个点
    我们对于每个点计算出其和其它点的交弧,对这些交弧计算起末位置对于圆心的极角,
    对这些我们进行扫描线操作,统计最大交集数量就是答案。
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
double EPS=1e-10;
double add(double a,double b){
    if(abs(a+b)<EPS*(abs(a)+abs(b)))return 0;
    return a+b;
}
const int MAX_N=310;
struct P{
    double x,y;
    P(){}
    P(double x,double y):x(x),y(y){}
    P operator + (P p){return P(add(x,p.x),add(y,p.y));}
    P operator - (P p){return P(add(x,-p.x),add(y,-p.y));}
    P operator * (double d){return P(x*d,y*d);}
    double dot(P p){return add(x*p.x,y*p.y);} //点积
    double det(P p){return add(x*p.y,-y*p.x);}  //叉积
}ps[MAX_N];
double dist(P p,P q){return sqrt((p-q).dot(p-q));}
struct PolarAngle{
    double angle;
    bool flag;
}as[MAX_N];
bool cmp_a(PolarAngle a,PolarAngle b){
	return a.angle<b.angle;
}
int solve(int n,double r){
    int ans=1;
    for(int i=0;i<n;i++){
        int m=0; double d;
        for(int j=0;j<n;j++){
            if(i!=j&&(d=dist(ps[i],ps[j]))<=2*r){
                double phi=acos(d/2);
                double theta=atan2(ps[j].y-ps[i].y,ps[j].x-ps[i].x);
                as[m].angle=theta-phi,as[m++].flag=1;
                as[m].angle=theta+phi,as[m++].flag=0;
            }
        }sort(as,as+m,cmp_a);
        for(int sum=1,j=0;j<m;j++){
            if(as[j].flag)sum++;
            else sum--;
            ans=max(ans,sum);
        }
    }return ans;
}
int N;
int main(){
    while(scanf("%d",&N),N){
        for(int i=0;i<N;i++)scanf("%lf%lf",&ps[i].x,&ps[i].y);
        printf("%d\n",solve(N,1.0));
    }return 0;
}

POJ 1418:Viva Confetti

/*
    给出一些圆的位置和半径以及叠放次序,问能看到的有几个圆
*/
#include <cstring>
#include <algorithm>
#include <cmath>
#include <complex>
#include <vector>
#include <cstdio>
using namespace std;
typedef complex<double> P;
#define M_PI 3.14159265358979323846
const double EPS=4E-13;
double Tran(double r){
    while(r<0.0)r+=2*M_PI;
    while(r>=2*M_PI)r-=2*M_PI;
    return r;
}
int hit_test(P p,vector<P>&points,vector<double> &rs){
    for(int i=rs.size()-1;i>=0;i--){
        if(abs(points[i]-p)<rs[i])return i;
    }return -1;
}
int n;
int main(){
    while(scanf("%d",&n),n){
        vector<P> points;
        vector<double> rs;
        for(int i=0;i<n;i++){
            double x,y,r;
            scanf("%lf%lf%lf",&x,&y,&r);
            points.push_back(P(x,y));
            rs.push_back(r);
        }vector<bool> visible(n,false);
        for(int i=0;i<n;i++){
            vector<double> rads;
            rads.push_back(0.0);
            rads.push_back(2.0*M_PI);
            for(int j=0;j<n;j++){
                double a=rs[i],b=abs(points[j]-points[i]),c=rs[j];
                if(a+b<c||a+c<b||b+c<a)continue;
                double d=arg(points[j]-points[i]);
                double e=acos((a*a+b*b-c*c)/(2*a*b));
                rads.push_back(Tran(d+e));
                rads.push_back(Tran(d-e));
            }sort(rads.begin(),rads.end());
			      for(int j=0;j<rads.size()-1;j++){
				        double rad=(rads[j+1]+rads[j])/2.0;
				        for(int k=-1;k<=1;k+=2){
					          int t=hit_test(P(points[i].real()+(rs[i]+EPS*k)*cos(rad),points[i].imag()+(rs[i]+EPS*k)*sin(rad)),points,rs);
					          if(t!=-1)visible[t]=true;
				        }
			      }
        }printf("%d\n",count(visible.begin(),visible.end(),true));
    }return 0;
}

AOJ 2201:Immortal Jewels

/*
    题目大意:给出一些圆,求一条直线最多与几个圆相切
    题解:我们枚举任意两个圆的切线,然后计算与这条切线相切的圆的数目即可。
*/
#include <iostream>
#include <vector>
#include <complex>
#include <cmath>
#include <algorithm>
using namespace std;
typedef complex<double> P;
const double PI=acos(-1);
const double EPS=1e-12;
int cmp(double a,double b){
	  const double diff=a-b;
	  if(fabs(diff)<EPS)return 0;
	  else if(diff<0)return -1;
	  else return 1;
}
inline double dot(const P &a, const P &b){
	  return a.real()*b.real()+a.imag()*b.imag();
}
inline double cross(const P &a, const P &b){
    return a.real()*b.imag()-b.real()*a.imag();
}
struct line{
    P a,b;
    line(){}
    line(const P &p,const P &q):a(p),b(q){}
	  // 是否平行
	  inline bool parallel(const line &ln) const{
		    return abs(cross(ln.b-ln.a,b-a))<EPS;
		    //平行叉乘得到向量的模是0,也就是sin(theta)=0<->theta=0
	  }
	  // 是否相交
	  inline bool intersects(const line &ln) const{
		    return !parallel(ln);
	  }
	  // 求交点
	  inline P intersection(const line &ln) const{
	      const P x=b-a;
	      const P y=ln.b-ln.a;
	      return a+x*(cross(y,ln.a-a))/cross(y,x);
	  }
	  // 点到直线的距离
	  inline double distance(const P &p) const{
	      return abs(cross(p-a,b-a))/abs(b-a);
	  }
	  // 求垂足坐标
	  inline P perpendicular(const P &p) const{
	      const double t=dot(p-a,a-b)/dot(b-a,b-a);
		    return a+t*(a-b);
		}
};
struct circle{
    P o;
    double r;
    circle(){}
    circle(const P &p,double x):o(p),r(x){}
    // 通过点 p 的两条切线
    pair<P,P> tangent(const P &p)const{
        const double L=abs(o-p);
        const double M=sqrt(L*L-r*r);
        const double theta=asin(r/L);
        const P v=(o-p)/L;
        return make_pair(p+M*(v*polar(1.0,theta)),p+M*(v*polar(1.0,-theta)));
	  }
	  // 两个半径相等圆的两条平行外切线
	  pair<line,line> outer_tangent_parallel(const circle &c) const{
	      const P d=o-c.o;
	      const P v=d*P(0,1)*r/abs(d);
	      return make_pair(line(o+v,c.o+v),line(o-v,c.o-v));
	  }
	  // 两个圆外切线
	  pair<line,line> outer_tangent(const circle &c) const{
	      if(cmp(r,c.r)==0)return outer_tangent_parallel(c);
	      if(r>c.r)return c.outer_tangent(*this);
	      const P d=o-c.o;
	      const double fact=c.r/r-1;
	      const P base=c.o+d+d/fact;
	      const pair<P, P> t=tangent(base);
	      return make_pair(line(base,t.first),line(base,t.second));
	  }
	  // 内切线
	  pair<line,line> inner_tangent(const circle &c) const{
	      if(r>c.r)return c.inner_tangent(*this);
	      const P d=c.o-o;
	      const double fact=c.r/r+1;
	      const P base=o+d/fact;
	      const pair<P,P> t=tangent(base);
	      return make_pair(line(base,t.first),line(base,t.second));
	  }
	  // 是否相交
	  inline bool intersects(const circle &c) const{
		    return !contains(c)&&!c.contains(*this)&&cmp(abs(o-c.o),r+c.r)<=0;
	  }
	  // 是否相离
	  inline bool independent(const circle &c) const{
	      return cmp(abs(o-c.o),r+c.r)>0;
	  }
	  // 两个圆的交点
	  pair<P,P> intersection(const circle &c) const{
	      const double d=abs(o-c.o);
	      const double cos_=(d*d+r*r-c.r*c.r)/(2*d);
	      const double sin_=sqrt(r*r-cos_*cos_);
	      const P e=(c.o-o)/d;
	      return make_pair(o+e*P(cos_,sin_),o+e* P(cos_,-sin_));
	  }
	  // 是否包含圆c
	  inline bool contains(const circle &c) const{
	      return cmp(abs(o-c.o)+c.r,r)<0;
	  }
	  // 是否相交
	  inline bool intersects(const line &ln) const{
	      return cmp(abs(ln.distance(o)),r)<=0;
	  }
	  // 圆心到直线的距离
	  inline double distance(const line &ln) const{
		    return abs(ln.distance(o));
	  }
	  // 圆与直线的交点
	  pair<P,P> intersection(const line &ln) const{
	      const P h=ln.perpendicular(o);
	      const double d=abs(h-o);
	      P ab=ln.b-ln.a;
	      ab/=abs(ab);
	      const double l=sqrt(r*r-d*d);
	      return make_pair(h+l*ab,h-l*ab);
	  }
};
void enum_event(const circle &c1,const circle &c2,vector<line> &lines){
	  if(c1.independent(c2)){
		    pair<line,line> outer=c1.outer_tangent(c2);
		    lines.push_back(outer.first);
		    lines.push_back(outer.second);
		    pair<line,line> inner = c1.inner_tangent(c2);
		    lines.push_back(inner.first);
		    lines.push_back(inner.second);
		}else if (c1.intersects(c2)){
		    pair<line,line> outer=c1.outer_tangent(c2);
		    lines.push_back(outer.first);
		    lines.push_back(outer.second);
		    pair<P,P> inter=c1.intersection(c2);
		    lines.push_back(line(inter.first,inter.second));
		    // 此时内切线不存在,使用交点形成的线代替
		}
}
bool solve(){
    int N;
    scanf("%d",&N);
    if(!N)return false;
    vector<pair<circle,circle> > jewels;
    vector<line> lines;
    for(int i=0;i<N;i++){
        double x,y,r,m;
        scanf("%lf%lf%lf%lf",&x,&y,&r,&m);
        const P center(x,y);
        pair<circle,circle> jewel=make_pair(circle(center,r),circle(center,r+m));
        for(const auto &other:jewels){
            enum_event(jewel.first,other.first,lines);
            enum_event(jewel.first,other.second,lines);
            enum_event(jewel.second,other.first,lines);
            enum_event(jewel.second,other.second,lines);
        }jewels.push_back(jewel);
    }int ans=1;
    for(auto &l:lines){
        int cnt=count_if(jewels.begin(),jewels.end(),[&](const pair<circle,circle> &j){	// [&] 按引用捕获在lambda表达式所在函数的函数体中提及的全部自动储存持续性变量
            return cmp(j.first.r, j.first.distance(l))<=0&&cmp(j.second.r,j.second.distance(l))>=0;	// 在磁力圆范围内且不在本体范围内
        });
        ans=max(ans, cnt);
    }printf("%d\n",ans);
    return 1;
}
int main(){
	  while(solve());
	  return 0;
}

POJ 3168:Barn Expansion

/*
    题目大意:给出一些矩形,没有相交和包含的情况,只有相切的情况
    问有多少个矩形没有相切或者边角重叠
    题解:我们将所有的与x轴平行的线段和与y周平行的线段分开处理,判断是否出现重合
    对重合的两个矩形进行标识,最后没有被标识过的矩形数目就是答案。
*/
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=30010;
struct data{
    int id,d,x,y;
    data(){};
    data(int _d,int _x,int _y,int _id):d(_d),x(_x),y(_y),id(_id){}
};
vector<data> sx,sy;
bool vis[N];
bool cmp(data a,data b){
    if(a.d!=b.d)return a.d<b.d;
    if(a.x!=b.x)return a.x<b.x;
    return a.y<b.y;
}
int n,a,b,c,d;
void solve(){
    sx.clear();sy.clear();
    memset(vis,0,sizeof(vis));
    for(int i=0;i<n;i++){
        scanf("%d%d%d%d",&a,&b,&c,&d);
        sy.push_back(data(b,a,c,i));
        sy.push_back(data(d,a,c,i));
        sx.push_back(data(a,b,d,i));
        sx.push_back(data(c,b,d,i));
    }sort(sx.begin(),sx.end(),cmp);
    sort(sy.begin(),sy.end(),cmp);
    int t=sy[0].y;
    for(int i=1;i<sy.size();i++){
        if(sy[i-1].d==sy[i].d){
            if(t>=sy[i].x){
                vis[sy[i].id]=vis[sy[i-1].id]=1;
            }
        }else t=sy[i].y;
        t=max(sy[i].y,t);
    }t=sx[0].y;
    for(int i=1;i<sx.size();i++){
        if(sx[i-1].d==sx[i].d){
            if(t>=sx[i].x){
                vis[sx[i].id]=vis[sx[i-1].id]=1;
            }
        }else t=sx[i].y;
        t=max(sx[i].y,t);
    }int ans=0;
    for(int i=0;i<n;i++)if(!vis[i])ans++;
    printf("%d\n",ans);
}
int main(){
    while(~scanf("%d",&n))solve();
    return 0;
}

POJ 3293:Rectilinear polygon

/*
    题目大意:给出一些点,每个点只能向外引出一条平行X轴,和Y轴的边,
    问能否构成一个闭多边形,如果能,返回多边形的总边长,否则返回-1
    题解:我们发现对于每一行或者每一列都必须有偶数个点,且两两之间相邻才能满足条件
    所以我们将其连线之后判断是否可以构成一个封闭图形,同时还需要判断这些线是否会相交,
    如果相交即不成立
*/
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=100010;
struct Point{int x,y,id;}p[N];
struct Line{
    int d,x,y;
    Line(){}
    Line(int _d,int _x,int _y):d(_d),x(_x),y(_y){}
}l[N];
int cmp_x(Point a,Point b){
    if(a.x==b.x)return a.y<b.y;
    return a.x<b.x;
}
int cmp_y(Point a,Point b){
    if(a.y==b.y)return a.x<b.x;
    return a.y<b.y;
}
int con[N][2],n,ln,T;
int Check(Point a,Point b){
    int y=a.y,x1=a.x,x2=b.x;
    for(int i=0;i<ln;i++){
        if(x1<l[i].d&&x2>l[i].d&&l[i].x<y&&l[i].y>y)return 1;
    }return 0;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d%d",&p[i].x,&p[i].y);
            p[i].id=i;
        }int s=0,cnt=1,flag=0;
        ln=0;
        sort(p,p+n,cmp_x);
        for(int i=1;i<n&&!flag;i++){
            if(p[i].x!=p[i-1].x){
                if(cnt&1)flag=1;
                cnt=1;
            }else{
                cnt++;
                if((cnt&1)==0){
                    s+=p[i].y-p[i-1].y;
                    con[p[i].id][0]=p[i-1].id;
                    con[p[i-1].id][0]=p[i].id;
                    l[ln++]=Line(p[i].x,p[i-1].y,p[i].y);
                }
            }
        }sort(p,p+n,cmp_y);
        cnt=1;
        for(int i=1;i<n&&!flag;i++){
            if(p[i].y!=p[i-1].y){
                if(cnt&1)flag=1;
                cnt=1;
            }
            else{
                cnt++;
                if((cnt&1)==0){
                    s+=p[i].x-p[i-1].x;
                    con[p[i].id][1]=p[i-1].id;
                    con[p[i-1].id][1]=p[i].id;
                    if(Check(p[i-1],p[i]))flag=1;
                }
            }
        }int t=1,x=0,c=0;
        for(;;){
            x=con[x][t];
            t^=1; c++;
            if(x==0||flag)break;
        }if(c!=n)flag=1;
        if(flag)puts("-1");
        else printf("%d\n",s);
    }return 0;
}

POJ 2482:Stars in Your Window

/*
    题目大意:给出一些点的二维坐标和权值,求用一个长H,宽W的矩形能框住的最大权值之和,
    在矩形边缘的点不计算在内
    题解:我们计算能扫到这个点的区间范围,将其拆分为两条平行于y轴的左闭右开的直线,
    为方便边界处理,我们将坐标扩大两倍,之后我们按照x轴对这些线段进行扫描
    统计出现的最大值即可。
*/
#include <cstdio>
#include <algorithm>
#include <utility>
using namespace std;
typedef long long LL;
const int N=10010;
LL xs[N],ys[N],X[N<<1],Y[N<<1];
int cs[N],tag[N<<3],T[N<<3];
pair<pair<int,int>,pair<int,int> >event[N<<1];
void update(int L,int R,int v,int x,int l,int r){
    if(L<=l&&r<=R){T[x]+=v;tag[x]+=v;return;}
    int mid=(l+r)>>1;
    if(L<=mid)update(L,R,v,x<<1,l,mid);
    if(mid<R)update(L,R,v,x<<1|1,mid+1,r);
    T[x]=max(T[x<<1],T[x<<1|1])+tag[x];
}
int n,W,H;
void solve(){
    for(int i=0;i<n;i++){
        scanf("%lld%lld%d",xs+i,ys+i,cs+i);
        xs[i]<<=1; ys[i]<<=1;
    }
    for(int i=0;i<n;i++){
        X[i<<1]=xs[i]-W; X[i<<1|1]=xs[i]+W;
        Y[i<<1]=ys[i]-H; Y[i<<1|1]=ys[i]-1+H;
    }sort(X,X+n*2);sort(Y,Y+n*2);
    for(int i=0;i<n;i++){
        event[i<<1]=make_pair(make_pair(lower_bound(X,X+n*2,xs[i]-W)-X,cs[i]),make_pair(lower_bound(Y,Y+n*2,ys[i]-H)-Y,lower_bound(Y,Y+n*2,ys[i]+H-1)-Y));
        event[i<<1|1]=make_pair(make_pair(lower_bound(X,X+n*2,xs[i]+W)-X,-cs[i]),make_pair(lower_bound(Y,Y+n*2,ys[i]-H)-Y,lower_bound(Y,Y+n*2,ys[i]+H-1)-Y));
    }sort(event,event+n*2);
		int ans=0;
		for(int i=0;i<n*2;i++){
			  update(event[i].second.first,event[i].second.second,event[i].first.second,1,0,n*2);
			  ans=max(ans,T[1]);
		}printf("%d\n",ans);
}
int main(){
    while(~scanf("%d%d%d",&n,&W,&H))solve();
    return 0;
}

POJ 1113:Wall

/*
    题目大意:给出一个城堡,要求求出距城堡距离大于L的地方建围墙将城堡围起来求所要围墙的长度
    题解:画图易得答案为凸包的周长加一个圆的周长。
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
double EPS=1e-10;
const double PI=acos(-1.0);
double add(double a,double b){
    if(abs(a+b)<EPS*(abs(a)+abs(b)))return 0;
    return a+b;
}
struct P{
    double x,y;
    P(){}
    P(double x,double y):x(x),y(y){}
    P operator + (P p){return P(add(x,p.x),add(y,p.y));}
    P operator - (P p){return P(add(x,-p.x),add(y,-p.y));}
    P operator * (double d){return P(x*d,y*d);}
    double dot(P p){return add(x*p.x,y*p.y);} //点积
    double det(P p){return add(x*p.y,-y*p.x);}  //叉积
};
bool cmp_x(const P& p,const P& q){
    if(p.x!=q.x)return p.x<q.x;
    return p.y<q.y;
}
vector<P> convex_hull(P* ps,int n){
    sort(ps,ps+n,cmp_x);
    int k=0;
    vector<P> qs(n*2);
    for(int i=0;i<n;i++){
        while((k>1)&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0)k--;
        qs[k++]=ps[i];
    }
    for(int i=n-2,t=k;i>=0;i--){
        while(k>t&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0)k--;
        qs[k++]=ps[i];
    }qs.resize(k-1);
    return qs;
}
double dist(P p,P q){return sqrt((p-q).dot(p-q));}
const int MAX_N=1000;
int N,L;
P ps[MAX_N];
vector<P> con;
void solve(){
    for(int i=0;i<N;i++)scanf("%lf%lf",&ps[i].x,&ps[i].y);
    con=convex_hull(ps,N);
    double res=0;
    for(int i=0;i<con.size()-1;i++)res+=dist(con[i],con[i+1]);
    res+=dist(con[0],con[con.size()-1]);
    res+=2*PI*L;
    printf("%d\n",(int)(res+0.5));
}
int main(){
    while(~scanf("%d%d",&N,&L))solve();
    return 0;
}

POJ 1912:A highway and the seven dwarfs

/*
    题目大意:给出一些点,表示一些屋子,这些屋子共同组成了村庄,现在要建一些高速公路
    问是否经过了村庄。
    题解:这些屋子的关键点一定在凸包上,所以我们只要求出凸包,判断是否和线相交即可
    我们求出与高速公路相近和近似相反的向量,判断连线是否与这条公路相交即可。
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
double EPS=1e-10;
const double PI=acos(-1.0);
double add(double a,double b){
    if(abs(a+b)<EPS*(abs(a)+abs(b)))return 0;
    return a+b;
}
struct P{
    double x,y;
    P(){}
    P(double x,double y):x(x),y(y){}
    P operator + (P p){return P(add(x,p.x),add(y,p.y));}
    P operator - (P p){return P(add(x,-p.x),add(y,-p.y));}
    P operator * (double d){return P(x*d,y*d);}
    double dot(P p){return add(x*p.x,y*p.y);} //点积
    double det(P p){return add(x*p.y,-y*p.x);}  //叉积
};
bool cmp_x(const P& p,const P& q){
    if(p.x!=q.x)return p.x<q.x;
    return p.y<q.y;
}
vector<P> convex_hull(P* ps,int n){
    sort(ps,ps+n,cmp_x);
    int k=0;
    vector<P> qs(n*2);
    for(int i=0;i<n;i++){
        while((k>1)&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0)k--;
        qs[k++]=ps[i];
    }
    for(int i=n-2,t=k;i>=0;i--){
        while(k>t&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0)k--;
        qs[k++]=ps[i];
    }qs.resize(k-1);
    return qs;
}
double dist(P p,P q){return sqrt((p-q).dot(p-q));}
double normalize(double r){
	  if(r<-PI/2.0+EPS)r+=PI*2;
	  return r;
}
double atan2(const P& p){
    return normalize(atan2(p.y, p.x));
}
bool double_cmp(double a,double b){
    return a+EPS<b;
}
const int MAX_N=100010;
int N,n;
P ps[MAX_N];
double as[MAX_N];
void solve(){
    for(int i=0;i<N;i++)scanf("%lf%lf",&ps[i].x,&ps[i].y);
    vector<P> chs;
    if(N>1){
        chs=convex_hull(ps,N);
        n=chs.size();
        chs.push_back(chs[0]);
    }
    for(int i=0;i<n;i++)as[i]=atan2(chs[i+1]-chs[i]);
    sort(as,as+n,double_cmp);
    P p1,p2;
    while(~scanf("%lf%lf%lf%lf",&p1.x,&p1.y,&p2.x,&p2.y)){
        if(N<2){puts("GOOD");continue;}
        int x=upper_bound(as,as+n,atan2(p2-p1),double_cmp)-as;
        int y=upper_bound(as,as+n,atan2(p1-p2),double_cmp)-as;
        puts((((p2-p1).det(chs[x]-p1)*(p2-p1).det(chs[y]-p1)>-EPS))?"GOOD":"BAD");
    }
}
int main(){
    while(~scanf("%d",&N))solve();
    return 0;
}

POJ 3608:Bridge Across Islands

/*
    题目大意:求出两个凸包之间的最短距离
    题解:我们先找到一个凸包的上顶点和一个凸包的下定点,以这两个点为起点向下一个点画线,
    做旋转卡壳,答案一定包含在这个过程中
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
double EPS=1e-10;
const double INF=0x3F3F3F3F;
const double PI=acos(-1.0);
double add(double a,double b){
    if(abs(a+b)<EPS*(abs(a)+abs(b)))return 0;
    return a+b;
}
struct P{
    double x,y;
    P(){}
    P(double x,double y):x(x),y(y){}
    P operator + (P p){return P(add(x,p.x),add(y,p.y));}
    P operator - (P p){return P(add(x,-p.x),add(y,-p.y));}
    P operator * (double d){return P(x*d,y*d);}
    double dot(P p){return add(x*p.x,y*p.y);} //点积
    double det(P p){return add(x*p.y,-y*p.x);}  //叉积
};
bool cmp_y(const P& p,const P& q){
    if(p.y!=q.y)return p.y<q.y;
    return p.x<q.x;
}
double dist(P p,P q){return sqrt((p-q).dot(p-q));}
double cross(P a, P b,P c){return(b-a).det(c-a);}
double multi(P a,P b,P c){return(b-a).dot(c-a);}
// 点到线段距离
double point_to_line(P a,P b,P c){
    if(dist(a,b)<EPS)return dist(b,c);
    if(multi(a,b,c)<-EPS)return dist(a,c);
    if(multi(b,a,c)<-EPS)return dist(b,c);
    return fabs(cross(a,b,c)/dist(a,b));
}
// 线段到线段距离
double line_to_line(P A,P B,P C,P D){
    double a=point_to_line(A,B,C);
    double b=point_to_line(A,B,D);
    double c=point_to_line(C,D,A);
    double d=point_to_line(C,D,B);
    return min(min(a,b),min(c,d));
}
void anticlockwise_sort(P* p,int N){
    for(int i=0;i<N-2;i++){
        double tmp=cross(p[i],p[i+1],p[i+2]);
        if(tmp>EPS)return;
        else if(tmp<-EPS){
            reverse(p,p+N);
            return;
        }
    }
}
const int MAX_N=10000;
int n,m;
P ps[MAX_N],qs[MAX_N];
void solve(){
    for(int i=0;i<n;i++)scanf("%lf%lf",&ps[i].x,&ps[i].y);
    for(int i=0;i<m;i++)scanf("%lf%lf",&qs[i].x,&qs[i].y);
    anticlockwise_sort(ps,n);
		anticlockwise_sort(qs,m);
		int i=0,j=0;
		for(int k=0;k<n;k++)if(!cmp_y(ps[i],ps[k]))i=k;
    for(int k=0;k<n;k++)if(cmp_y(qs[j],qs[k]))j=k;
    double res=INF;
    ps[n]=ps[0]; qs[m]=qs[0];
    for(int k=0;k<n;k++){
    	while(cross(ps[i+1],qs[j+1],ps[i])-cross(ps[i+1],qs[j],ps[i])>EPS)j=(j+1)%m;
        res=min(res,line_to_line(ps[i],ps[i+1],qs[j],qs[j+1]));
        i=(i+1)%n;
    }printf("%.5lf\n",res);
}
int main(){
    while(~scanf("%d%d",&n,&m)&&n+m)solve();
    return 0;
}

POJ 2079:Triangle

/*
    题目大意:给出一些点,求出能组成的最大面积的三角形
    题解:最大三角形一定位于凸包上,因此我们先求凸包,再在凸包上计算,
    因为三角形在枚举了一条固定边之后,图形面积随着另一个点的位置变换先变大后变小
    因此我们发现面积递减之后就移动固定边。
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
double EPS=1e-10;
const double PI=acos(-1.0);
double add(double a,double b){
    if(abs(a+b)<EPS*(abs(a)+abs(b)))return 0;
    return a+b;
}
struct P{
    double x,y;
    P(){}
    P(double x,double y):x(x),y(y){}
    P operator + (P p){return P(add(x,p.x),add(y,p.y));}
    P operator - (P p){return P(add(x,-p.x),add(y,-p.y));}
    P operator * (double d){return P(x*d,y*d);}
    double dot(P p){return add(x*p.x,y*p.y);} //点积
    double det(P p){return add(x*p.y,-y*p.x);}  //叉积
};
bool cmp_x(const P& p,const P& q){
    if(p.x!=q.x)return p.x<q.x;
    return p.y<q.y;
}
vector<P> convex_hull(P* ps,int n){
    sort(ps,ps+n,cmp_x);
    int k=0;
    vector<P> qs(n*2);
    for(int i=0;i<n;i++){
        while((k>1)&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0)k--;
        qs[k++]=ps[i];
    }
    for(int i=n-2,t=k;i>=0;i--){
        while(k>t&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0)k--;
        qs[k++]=ps[i];
    }qs.resize(k-1);
    return qs;
}
double cross(P A,P B,P C){return(B-A).det(C-A);}
const int MAX_N=50010;
int N;
P ps[MAX_N];
void solve(){
    for(int i=0;i<N;i++)scanf("%lf%lf",&ps[i].x,&ps[i].y);
    vector<P> qs=convex_hull(ps,N);
    N=qs.size(); int ans=0;
    for(int i=1;i<(N+1)/2;i++){ //枚举跨度
        int p1=(i+1)%N;
        for(int p3=0;p3<N;p3++){
            int p2=(p3+i)%N;
            int prev=abs(cross(qs[p3],qs[p2],qs[p1]));
            for(++p1;p1!=p2&&p1!=p3;++p1){
                if(p1==N)p1=0;
                int cur=abs(cross(qs[p3],qs[p2],qs[p1]));
                ans=max(ans,prev);
                if(cur<=prev)break;
                prev=cur;
            }--p1;
            if(p1==-1)p1+=N;
        }
    }printf("%d.%s\n",ans/2,ans%2==1?"50":"00");
}
int main(){
    while(~scanf("%d",&N)&&N>0)solve();
    return 0;
}

POJ 3246:Game

/*
    题目大意:给出一些点,请删去一个点,使得包围这些点用的线长最短
    题解:去掉的点肯定是凸包上的点,所以枚举凸包上的点去掉,再计算面积即可。
*/
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
using namespace std;
struct P{
    int x,y;
    int id;
    P(){}
    P(double x,double y):x(x),y(y){}
    P operator + (P p){return P(x+p.x,y+p.y);}
    P operator - (P p){return P(x-p.x,y-p.y);}
    P operator * (double d){return P(x*d,y*d);}
    int dot(P p){return x*p.x+y*p.y;} //点积
    int det(P p){return x*p.y-y*p.x;}  //叉积
};
bool cmp_x(const P& p,const P& q){
    if(p.x!=q.x)return p.x<q.x;
    return p.y<q.y;
}
vector<P> convex_hull(P* ps,int n){
    sort(ps,ps+n,cmp_x);
    int k=0;
    vector<P> qs(n*2);
    for(int i=0;i<n;i++){
        while((k>1)&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0)k--;
        qs[k++]=ps[i];
    }
    for(int i=n-2,t=k;i>=0;i--){
        while(k>t&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0)k--;
        qs[k++]=ps[i];
    }qs.resize(k-1);
    return qs;
}
int cross(P a, P b,P c){return(b-a).det(c-a);}
int compute_area(P A,P B,P C){
    int res=cross(A,B,C);
    if(res<0){return -res;}
    return res;
}
int compute_area(const vector<P>& ps){
    int total=0;
    for(int i=2;i<ps.size();i++){
        total+=compute_area(ps[0],ps[i-1],ps[i]);
    }return total;
}
const int MAX_N=100010;
int N;
P p[MAX_N],q[MAX_N];
void solve(){
    for(int i=0;i<N;i++){
        scanf("%d%d",&p[i].x,&p[i].y);
        p[i].id=i;
    }memcpy(q,p,N*sizeof(P));
    vector<P> ps=convex_hull(p,N);
    int ans=0x3f3f3f3f;
    for(int i=0;i<ps.size();i++){
        memcpy(p,q,N*sizeof(P));
        swap(p[ps[i].id],p[N-1]);
        ans=min(ans,compute_area(convex_hull(p,N-1)));
    }printf("%d.%s\n",ans/2,ans%2==1?"50":"00");
}
int main(){
    while(~scanf("%d",&N),N)solve();
    return 0;
}
时间: 2024-10-19 00:04:16

挑战程序设计竞赛 3.6 与平面和空间打交道的计算几何的相关文章

与平面和空间打交道的计算几何

计算几何基础 Jack Straws(POJ 1127) 原题如下: Jack Straws Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 5494 Accepted: 2504 Description In the game of Jack Straws, a number of plastic or wooden "straws" are dumped on the table and players try

POJ 2836 Rectangular Covering 题解 《挑战程序设计竞赛》

POJ 2836 Rectangular Covering铺地板:坐标平面上有n各点,用任意大小(非零)的地板砖覆盖它们,求最省的地板砖总面积.3.4熟练掌握动态规划状态压缩DP先预处理数据,将n个点两两组合形成n * (n-1) / 2个矩形,计算每个矩形的面积和内部点个数.接着利用预处理数据来枚举,定义dp[S] := 矩形集为S时的最省面积先假设平面上没有矩形,那么dp[0]=0,接着一个一个地往平面上加矩形,递推关系是:dp[新矩形集合] = min(dp[新矩形集合], dp[旧矩形集

POJ 3420 Quad Tiling 题解 《挑战程序设计竞赛》

POJ 3420 Quad Tiling贴瓷砖:4*N的地板上用2*1的瓷砖铺满,求所有方案数对M求余.3.4熟练掌握动态规划矩阵的幂久违地上了节课,太无聊,只好刷一题.假设S[n]表示填满n时的方案数,有S[0]=1.定义矩阵M[p][q] := 边缘p和边缘q可以拼合时取1,否则取0所谓的可以拼合表示,两个边缘拼起来后长度为1(为2就拼接不起来了),且内部缝隙可以用2*1的瓷砖填满.那么M就有一些简单的性质了,比如M的第一行应该是:0 0 0 0 0 0... 继续阅读:码农场 » POJ

POJ 3411 Paid Roads 题解 《挑战程序设计竞赛》

POJ 3411 Paid Roads开路:N个城市间有m条单向路,分别从a到b,可以在c处交P路费,也可以直接交R路费.那么问题来了,你的挖掘机怎么开最省钱?3.4熟练掌握动态规划状态压缩DP乍一看可以Dijkstra,实际上的确可以Dijkstra.不过多了一个预交费的c,所以在遍历的时候多了一维状态,这个维度储存当前走过的城市集合.在Dijkstra的时候,如果走过了c那么就有两个选择,选其中最省的即可:否则没得选.#include <iostream> #include&nb.

字符串HASH模板 取自挑战程序设计竞赛(第2版)

/*===================================================* 从b串中寻找和a串长度相同的子串,返回开始位置 不保证绝对正确,发生冲突概率为O(sqrt(n)), n为哈希函数的最大值 \*===================================================*/ #define ull unsigned long long const ull B = 1e8+7; /*according to the book*/

[转] AOJ 0525 Osenbei《挑战程序设计竞赛(第2版)》练习题答案

来自 码农场 ? AOJ 0525 Osenbei<挑战程序设计竞赛(第2版)>练习题答案 只把代码复制过来,原博的其他分析请看链接. 1 #include <iostream> 2 #include <bitset> 3 #include <algorithm> 4 5 using namespace std; 6 7 bitset<10000> cookie[10]; 8 9 ///////////////////////////SubMai

poj2431 Expedition (优先队列) 挑战程序设计竞赛

题目链接:http://poj.org/problem?id=2431 Expedition Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9148   Accepted: 2673 Description A group of cows grabbed a truck and ventured on an expedition deep into the jungle. Being rather poor driver

Aizu 2249Road Construction 单源最短路变形《挑战程序设计竞赛》模板题

King Mercer is the king of ACM kingdom. There are one capital and some cities in his kingdom. Amazingly, there are no roads in the kingdom now. Recently, he planned to construct roads between the capital and the cities, but it turned out that the con

38天刷完挑战程序设计竞赛&amp;数论概论计划

现在是2017年12月6日22:03:36,在做数论概论的chapter6线性方程与最大公因数的习题时,粗略地翻了一下挑战程序设计竞赛,感觉相见恨晚:遂决定38天刷完挑战程序设计竞赛和数论概论. 时间:2017年12月7日00:00:00 -- 2018年1月15日00:00:00 目前进度:数论概论完成前6章,挑战程序设计竞赛完成0 目标:刷完数论概论44章,理解所有定理和证明,刷完全部练习 刷完挑战程序设计竞赛4章共406页,理解所有知识点和证明,刷完全部例题和习题(此目标可以调整,比如跳过