UVALive 4795 Paperweight(3D-点面距离-投影)

题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2796

题意:给出一个5个顶点的多面体以及多面体内一点P。求让多面体不同的方式(即以不同的面)放在地面上,设这个着地的面为A,多面体重心在A上的投影为B,在保证B在A内部且距离A的各个边界不小于0.2的前提下(否则这种放置方式就是不合法的),求P距离地面的最大最小距离为多少。

思路:本题需要这些计算:

(1)判断两个点是不是在面的同一侧;否则这个面就不能作为着地的面;

(2)计算重心;

(3)计算点在面的投影;

(4)计算点是否在面内;

(5)计算点到线的距离;

(6)计算点到面的距离。

 


#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <map>
#include <time.h>






#define abs(x) ((x)>=0?(x):-(x))
#define i64 long long
#define u32 unsigned int
#define u64 unsigned long long
#define clr(x,y) memset(x,y,sizeof(x))
#define CLR(x) x.clear()
#define ph(x) push(x)
#define pb(x) push_back(x)
#define Len(x) x.length()
#define SZ(x) x.size()
#define PI acos(-1.0)
#define sqr(x) ((x)*(x))
#define MP(x,y) make_pair(x,y)
#define EPS 1e-6


#define FOR0(i,x) for(i=0;i<x;i++)
#define FOR1(i,x) for(i=1;i<=x;i++)
#define FOR(i,a,b) for(i=a;i<=b;i++)
#define FORL0(i,a) for(i=a;i>=0;i--)
#define FORL1(i,a) for(i=a;i>=1;i--)
#define FORL(i,a,b)for(i=a;i>=b;i--)


#define rush() int CC;for(scanf("%d",&CC);CC--;)
#define Rush(n)  while(scanf("%d",&n)!=-1)
using namespace std;


void RD(int &x){scanf("%d",&x);}
void RD(i64 &x){scanf("%lld",&x);}
void RD(u64 &x){scanf("%I64u",&x);}
void RD(u32 &x){scanf("%u",&x);}
void RD(double &x){scanf("%lf",&x);}
void RD(int &x,int &y){scanf("%d%d",&x,&y);}
void RD(i64 &x,i64 &y){scanf("%lld%lld",&x,&y);}
void RD(u32 &x,u32 &y){scanf("%u%u",&x,&y);}
void RD(double &x,double &y){scanf("%lf%lf",&x,&y);}
void RD(double &x,double &y,double &z){scanf("%lf%lf%lf",&x,&y,&z);}
void RD(int &x,int &y,int &z){scanf("%d%d%d",&x,&y,&z);}
void RD(i64 &x,i64 &y,i64 &z){scanf("%lld%lld%lld",&x,&y,&z);}
void RD(u32 &x,u32 &y,u32 &z){scanf("%u%u%u",&x,&y,&z);}
void RD(char &x){x=getchar();}
void RD(char *s){scanf("%s",s);}
void RD(string &s){cin>>s;}


void PR(int x) {printf("%d\n",x);}
void PR(int x,int y) {printf("%d %d\n",x,y);}
void PR(i64 x) {printf("%lld\n",x);}
void PR(i64 x,i64 y) {printf("%lld %lld\n",x,y);}
void PR(u32 x) {printf("%u\n",x);}
void PR(u64 x) {printf("%llu\n",x);}
void PR(double x) {printf("%.10lf\n",x);}
void PR(double x,double y) {printf("%.5lf %.5lf\n",x,y);}
void PR(char x) {printf("%c\n",x);}
void PR(char *x) {printf("%s\n",x);}
void PR(string x) {cout<<x<<endl;}


const int mod=10007;
const i64 inf=((i64)1)<<40;
const double dinf=1000000000000000000.0;
const int INF=100000000;
const int N=105;


int sgn(double x)
{
    if(x>EPS) return 1;
    if(x<-EPS) return -1;
    return 0;
}


struct point
{
    double x,y;
    
        
    point(){}
    point(double _x,double _y)
    {
        x=_x;
        y=_y;
    }
    
    void get()
    {
        RD(x); RD(y);
    }
    
    point operator+(point a)
    {
        return point(x+a.x,y+a.y);
    }
    
    point operator-(point a)
    {
        return point(x-a.x,y-a.y);
    }
    
    double operator*(point a)
    {
        return x*a.y-y*a.x;
    }
    
    point operator*(double t)
    {
        return point(x*t,y*t);
    }
    
    double operator^(point a)
    {
        return x*a.x+y*a.y;
    }
    
    double len()
    {
        return sqrt(x*x+y*y);
    }
    
    void print()
    {
        printf("%.3lf %.3lf\n",x+EPS,y+EPS);
    }
};


double len(point a)
{
    return a.len();
}


struct point3
{
    double x,y,z;
    
    point3(){}
    point3(double _x,double _y,double _z)
    {
        x=_x;
        y=_y;
        z=_z;
    }
    
    void get()
    {
        cin>>x>>y>>z;
    }
    
    point3 operator+(point3 a)
    {
        return point3(x+a.x,y+a.y,z+a.z);
    }
    
    point3 operator-(point3 a)
    {
        return point3(x-a.x,y-a.y,z-a.z);
    }
    
    point3 operator*(point3 a)
    {
        return point3(y*a.z-z*a.y,z*a.x-x*a.z,x*a.y-y*a.x);
    }
    
    point3 operator*(double t)
    {
        return point3(x*t,y*t,z*t);
    }
    
    double operator^(point3 a)
    {
        return x*a.x+y*a.y+z*a.z;
    }
    
    point3 operator/(double t)
    {
        return point3(x/t,y/t,z/t);
    }
    
    double len()
    {
        return sqrt(x*x+y*y+z*z);
    }
    
    point3 adjust(double L)
    {
        double t=len();
        L/=t;
        return point3(x*L,y*L,z*L);
    }
    
    void print()
    {
        printf("%.10lf %.10lf %.10lf\n",x+EPS,y+EPS,z+EPS);
    }
};




double len(point3 a)
{
    return a.len();
}




double getArea(point3 a,point3 b,point3 c)
{
    double x=len((b-a)*(c-a));
    return x/2;
}
    
double getVolume(point3 a,point3 b,point3 c,point3 d)
{
    double x=(b-a)*(c-a)^(d-a);
    return x/6;
}


point3 pShadowOnPlane(point3 p,point3 a,point3 b,point3 c)
{
    point3 v=(b-a)*(c-a);
    if(sgn(v^(a-p))<0) v=v*-1;
    v=v.adjust(1);
    double d=fabs(v^(a-p));
    return p+v*d;
}




int pInPlane(point3 p,point3 a,point3 b,point3 c)
{
    double S=getArea(a,b,c);
    double S1=getArea(a,b,p);
    double S2=getArea(a,c,p);
    double S3=getArea(b,c,p);
    return sgn(S-S1-S2-S3)==0;
}


int opposite(point3 p,point3 q,point3 a,point3 b,point3 c)
{
    point3 v=(b-a)*(c-a);
    double x=v^(p-a);
    double y=v^(q-a);
    return sgn(x*y)<0;
}


int segCrossTri(point3 p,point3 q,point3 a,point3 b,point3 c)
{
    return opposite(p,q,a,b,c)&&
            opposite(a,b,p,q,c)&&
            opposite(a,c,p,q,b)&&
            opposite(b,c,p,q,a);
}


double pToPlane(point3 p,point3 a,point3 b,point3 c)
{
    double v=((b-a)*(c-a)^(p-a))/6;
    double s=len((b-a)*(c-a))/2;
    return fabs(3*v/s);
}


double pToLine(point3 p,point3 a,point3 b)
{
    double S=len((a-p)*(b-p));
    return S/len(a-b);
}


struct face
{
    int a,b,c,ok;
    
    face(){}
    face(int _a,int _b,int _c,int _ok)
    {
        a=_a;
        b=_b;
        c=_c;
        ok=_ok;
    }
};


struct _3DCH
{
    face F[N<<2];
    int b[N][N],cnt,n;
    point3 p[N];
    
    int getDir(point3 t,face F)
    {
        double x=(p[F.b]-p[F.a])*(p[F.c]-p[F.a])^(t-p[F.a]);
        return sgn(x);
    }
    
    
    void deal(int i,int x,int y)
    {
        int f=b[x][y];
        if(!F[f].ok) return;
        if(getDir(p[i],F[f])==1) DFS(i,f);
        else
        {
            b[y][x]=b[x][i]=b[i][y]=cnt;
            F[cnt++]=face(y,x,i,1);
        }
    }
    
    void DFS(int i,int j)
    {
        F[j].ok=0;
        deal(i,F[j].b,F[j].a);
        deal(i,F[j].c,F[j].b);
        deal(i,F[j].a,F[j].c);
    }
    
    void construct()
    {
        int i,j,k=0;
        for(i=1;i<n;i++) if(sgn(len(p[i]-p[0])))
        {
            swap(p[i],p[1]);
            k++;
            break;
        }
        if(k!=1) return;
        for(i=2;i<n;i++) if(sgn(getArea(p[0],p[1],p[i])))
        {
            swap(p[i],p[2]);
            k++;
            break;
        }
        if(k!=2) return;
        for(i=3;i<n;i++) if(sgn(getVolume(p[0],p[1],p[2],p[i])))
        {
            swap(p[i],p[3]);
            k++;
            break;
        }
        if(k!=3) return;
        
        cnt=0;
        FOR0(i,4)
        {
            face k=face((i+1)%4,(i+2)%4,(i+3)%4,1);
            if(getDir(p[i],k)==1) swap(k.b,k.c);
            b[k.a][k.b]=b[k.b][k.c]=b[k.c][k.a]=cnt;
            F[cnt++]=k;
        }
        
        for(i=4;i<n;i++) FOR0(j,cnt)
        {
            if(F[j].ok&&getDir(p[i],F[j])==1)
            {
                DFS(i,j);
                break;
            }
        }
        j=0;
        FOR0(i,cnt) if(F[i].ok) F[j++]=F[i];
        cnt=j;
    }
    
    point3 getCenter()
    {
        point3 ans=point3(0,0,0),o=point3(0,0,0);
        double s=0,temp;
        int i;
        FOR0(i,cnt)
        {
            face k=F[i];
            temp=getVolume(o,p[k.a],p[k.b],p[k.c]);
            ans=ans+(o+p[k.a]+p[k.b]+p[k.c])/4*temp;
            s+=temp;
        }
        ans=ans/s;
        return ans;
    }
    
    double getMinDis(point3 a)
    {
        double ans=dinf;
        int i;
        FOR0(i,cnt) 
        {
            face k=F[i];
            ans=min(ans,pToPlane(a,p[k.a],p[k.b],p[k.c]));
        }
        return ans;
    }
    
};




/********************模板*************************/


point3 C,F,p[5];
double Min,Max;


int check(point3 a,point3 b,point3 c)
{
    point3 k=pShadowOnPlane(C,a,b,c);
    if(!pInPlane(k,a,b,c)) return 0;
    if(sgn(pToLine(k,a,b)-0.2)<0) return 0;
    if(sgn(pToLine(k,c,b)-0.2)<0) return 0;
    if(sgn(pToLine(k,a,c)-0.2)<0) return 0;
    return 1;
}


int check(point3 a,point3 b,point3 c,point3 d)
{
    point3 k=pShadowOnPlane(C,a,b,c);
    if(!pInPlane(k,a,b,c)&&!pInPlane(k,a,d,c)) return 0;
    if(sgn(pToLine(k,a,b)-0.2)<0) return 0;
    if(sgn(pToLine(k,c,b)-0.2)<0) return 0;
    if(sgn(pToLine(k,d,c)-0.2)<0) return 0;
    if(sgn(pToLine(k,a,d)-0.2)<0) return 0;
    return 1;
}


int ok(int a,int b,int c)
{
    int x=-1,y=-1,i;
    FOR0(i,5)
    {
        if(i!=a&&i!=b&&i!=c)
        {
            if(x==-1) x=i;
            else y=i;
        }
    }
    return !opposite(p[x],p[y],p[a],p[b],p[c]);
}


void up(point3 a,point3 b,point3 c)
{
    double h=pToPlane(F,a,b,c);
    Min=min(Min,h);
    Max=max(Max,h);
}




int main()
{
    int num=0;
    while(1)
    {
        int i,j,k,t;
        FOR0(i,5) p[i].get();
        F.get();
        if(!cin.good()) break;
        
        double v1=fabs(getVolume(p[0],p[1],p[2],p[3]));
        double v2=fabs(getVolume(p[0],p[1],p[2],p[4]));
        point3 c1=(p[0]+p[1]+p[2]+p[3])/4*v1;
        point3 c2=(p[0]+p[1]+p[2]+p[4])/4*v2;
        C=(c1+c2)/(v1+v2);
        
        Min=dinf; Max=-dinf;
        FOR0(i,5) FOR0(j,5) FOR0(k,5)
        {
            if(i!=j&&i!=k&&j!=k&&ok(i,j,k))
            {
                if(check(p[i],p[j],p[k])) up(p[i],p[j],p[k]);
                FOR0(t,5) if(t!=i&&t!=j&&t!=k)
                {
                    c1=(p[j]-p[i])*(p[k]-p[i]);
                    c2=(p[j]-p[i])*(p[t]-p[i]);
                    if(sgn(len(c1*c2))==0&&check(p[i],p[j],p[k],p[t]))
                    {
                        up(p[i],p[j],p[k]);
                    }
                }
            }
        }
        printf("Case %d: ",++num);
        PR(Min,Max);
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值