hdu 5839(空间几何 网络选拔赛)

题意:
在三维空间给出若干个点,在这些点中选取四个点,组成四面体,满足这个四面体中至少有四条边是相等的。并且另两条不相等的边必须不相邻。

解题思路:
就是找出两条边,将这两条边作为四面体的对边。枚举第一条边找第二条边。找到一个平面经过第一条边的中点,且与第一条边垂直,第二条直线肯定在次平面内。所以枚举第一条直线的两个点,并且找到上述的平面,再遍历所有的点,找出在此平面上的点。在这些点中任意找两个点判断是否满足条件。满足条件的四面体分两种情况,一种是四条边相等,第二种是六条边相等。五条边相对的与四条边相等的情况一样。分这两种情况因为六条边相等的情况会被重复计算六次,四条边相等的被重复算两次。然后分开统计,之后出一下就是答案了。
(本来赛时是有思想的,结果实现的时候有一些情况忘记判断了,比如刚开始两条直线的重点重合也被统计了)

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#define eps 1e-11
using namespace std;
typedef long long ll;
ll neq,eq;
ll ans;
int n;
struct node
{
    double x,y,z;
}p[300];

bool cmp(node a,node b)
{
    if(fabs(a.x-b.x)<=eps)
    {
        if(fabs(a.y-b.y)<=eps)
            return a.z<b.z;
        return a.y<b.y;
    }
    return a.x<b.x;
}

double juli(node a,node b)///两点之间的距离
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z);
}
void work(node a,node b){
    int getin[300];///
    int cnt=0;///统计在第一条边找到的平面上的点的个数
    double dis1=juli(a,b);
    double dis2;
    node mid;///第一条边中点
    mid.x=(a.x+b.x)/2;
    mid.y=(a.y+b.y)/2,mid.z=(a.z+b.z)/2;
    double k1,k2,k3,d;
    k1=a.x-b.x,k2=a.y-b.y,k3=a.z-b.z;
    d=k1*mid.x+k2*mid.y+k3*mid.z;///队友说这是法向量跟一个点确定一个平面
    for(int i=0;i<n;i++)
    {
        if(fabs(k1*p[i].x+k2*p[i].y+k3*p[i].z-d)<=eps)///判断是否在平面上
            getin[cnt++]=i;///在平面上的下标存储下来
    }
    if(cnt<1)
        return ;
    node mid2;///满足条件的第二条边的中点
    double m1,m2,m3;
    double l1,l2,l3;
    for(int i=0;i<cnt;i++)
    {
        for(int j=i+1;j<cnt;j++)
        {
            mid2.x=(p[getin[i]].x+p[getin[j]].x)/2;
            mid2.y=(p[getin[i]].y+p[getin[j]].y)/2;
            mid2.z=(p[getin[i]].z+p[getin[j]].z)/2;
            m1=mid2.x-mid.x;m2=mid2.y-mid.y;m3=mid2.z-mid.z;
            l1=p[getin[i]].x-p[getin[j]].x;
            l2=p[getin[i]].y-p[getin[j]].y;
            l3=p[getin[i]].z-p[getin[j]].z;
            if(fabs(m1*l1+m2*l2+m3*l3)<=eps)
            {
                if(fabs(m1)<eps&&fabs(m2)<eps&&fabs(m3)<eps)
                    ///当找到的两条直线的中点重合不满足条件
                {
                    continue;
                }
                dis2=juli(p[getin[i]],p[getin[j]]);
                if(fabs((dis1-dis2))<=eps&&fabs(juli(a,p[getin[i]])-juli(a,b))<=eps)
                    eq++;///六条边相等的情况
                else 
                    neq++;///非六条边相等的情况
            }
        }
    }

}
int main()
{
    int cas=0;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        eq=0,neq=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
        }
        sort(p,p+n,cmp);///排序下面的循环进行除重
        int nn=1;
        for(int i=1;i<n;i++)
        {
            if((fabs(p[i].x-p[i-1].x)<=eps)&&(fabs(p[i].y-p[i-1].y)<=eps)&&(fabs(p[i].z-p[i-1].z)<=eps))
                continue;
            else
            {
                p[nn].x=p[i].x;
                p[nn].y=p[i].y;
                p[nn].z=p[i].z;
                nn++;
            }
        }
        n=nn;
        if(n<4)
        {
            printf("Case #%d: 0\n",++cas);
            continue;
        }
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)///枚举第一条边的两个点
            {
                work(p[i],p[j]);
            }
        }
        ans=eq/6+neq/2;
        printf("Case #%d: %I64d\n",++cas,ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值