bzoj 1209: [HNOI2004]最佳包裹 三维凸包

1209: [HNOI2004]最佳包裹

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 160  Solved: 58
[Submit][Status][Discuss]

Description

H
公司生产了一种金属制品,是由一些笔直的金属条支撑起来的,金属条和别的金属条在交点上被焊接在了一起。现在由于美观需要,在这个产品用一层特殊的材料包
裹起来。公司为了节约成本,希望消耗的材料最少(不计裁剪时的边角料的损失)。你的程序需要根据给定的输入,给出符合题意的输出:?
输入包括该产品的顶点的个数,以及所有顶点的坐标;? 你需要根据输入的计算出包裹这个产品所需要的材料的最小面积。?
结果要求精确到小数点后第六位。(四舍五入)

Input

第1行是一个整数n(4 <= n <= 100),表示顶点的个数;第2行到第n+1行,每行是3个实数xi,yi,zi,表示第i个顶点的坐标。每个顶点的位置各不相同。

Output

输出只有一个实数,表示包裹一个该产品所需的材料面积的最小值。

Sample Input

4
0 0 0
1 0 0
0 1 0
0 0 1 说明:该输入示例中共有4个点,可参见后面的图示。

Sample Output

2.366025

HINT

  这道题算是三维凸包的模板题吧,话说网上的三维凸包的教程中判三点共线,四点共面的方法确实有些复杂,经过idy博客的启发,其实在做凸包前对每个点加一遍噪音就行了。如果一个点可以看见当前一个面,那么这个面就会被删除,其表现就是有向体积为负数,删除一个面的同时,删除这个面的三条边(也是有向的)最有扫一遍观察哪些边的反向边不存在,然后将他们的反向边与当前加的点一同构成新的凸包。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<assert.h>
#include<vector>
using namespace std;
#define MAXN 120
#define eps 1e-11
typedef double real;
inline int sgn(real x)
{
        if (abs(x)<eps)return 0;
        return x<0?-1:1;
}
struct point
{
        real x,y,z;
        point(real x,real y,real z):x(x),y(y),z(z){}
        point(){}
        int read()
        {
                return scanf("%lf %lf %lf\n",&x,&y,&z);
        }
        void noise()
        {
                x+=(real)(rand()%10000-5000)/10000*eps;
                y+=(real)(rand()%10000-5000)/10000*eps;
                z+=(real)(rand()%10000-5000)/10000*eps;
        }
        real len()
        {
                return sqrt(x*x+y*y+z*z);
        }
};
typedef point vect;
struct line
{
        point ps;
        real x,y,z;
        line(){}
        line(point p1,point p2)
        {
                ps=p1;
                x=p2.x-p1.x;
                y=p2.y-p1.y;
                z=p2.z-p1.z;
        }
        line(point ps,real x,real y,real z):ps(ps),x(x),y(y),z(z){}
};
vect xmul(line l1,line l2)
{
        return vect(l1.y*l2.z-l1.z*l2.y,l1.z*l2.x-l1.x*l2.z,l1.x*l2.y-l1.y*l2.x);
}
real volume(line l1,line l2,line l3)
{
        return + l1.x*l2.y*l3.z - l1.x*l2.z*l3.y
               - l1.y*l2.x*l3.z + l1.y*l2.z*l3.x
               + l1.z*l2.x*l3.y - l1.z*l2.y*l3.x;
}
//+1 2 3
//-1 3 2
//-2 1 3
//+2 3 1
//+3 1 2
//-3 2 1
struct surface
{
        point ps;
        real x1,y1,z1;
        real x2,y2,z2;
        surface(){}
        surface(point p1,point p2,point p3)
        {
                ps=p1;
                x1=p2.x-p1.x,y1=p2.y-p1.y,z1=p2.z-p1.z;
                x2=p3.x-p1.x,y2=p3.y-p1.y,z2=p3.z-p1.z;
        }
        real volume(point pt)
        {
                return ::volume(line(ps,pt),line(ps,x1,y1,z1),line(ps,x2,y2,z2));
        }
        vect nvect()
        {
                return xmul(line(ps,x1,y1,z1),line(ps,x2,y2,z2));
        }
        void reverse()
        {
                swap(x1,x2);
                swap(y1,y2);
                swap(z1,z2);
        }
};
point pl[MAXN];
struct face
{
        int pt[3];
        face(int x,int y,int z)
        {
                pt[0]=x;pt[1]=y;pt[2]=z;
        }
        surface ToSurface()
        {
                return surface(pl[pt[0]],pl[pt[1]],pl[pt[2]]);
        }
        void print()
        {
                printf("Face:%d %d %d\n",pt[0],pt[1],pt[2]);
        }
};
vector<face> cc;
vector<pair<int,int> > chs;
bool status[MAXN][MAXN];
int main()
{
        freopen("input.txt","r",stdin);
        int n;
        scanf("%d",&n);
        for (int i=0;i<n;i++)
                pl[i].read();
        for (int i=0;i<n;i++)
                pl[i].noise();
    /*    for (int i=0;i<n;i++)
                swap(pl[rand()%n],pl[rand()%n]);*/
        cc.push_back(face(0,1,2));
        cc.push_back(face(2,1,0));
        for (int i=0;i<3;i++)
                status[i][(i+1)%3]=true;
        for (int i=1;i<4;i++)
                status[i%3][i-1]=true;
        for (int i=3;i<n;i++)
        {
                //for (int j=0;j<cc.size();j++)cc[j].print();    printf("\n");
                chs.clear();
                for (int j=0;j<cc.size();j++)
                {
                        if (cc[j].ToSurface().volume(pl[i])<0)
                        {
                                for (int k=0;k<3;k++)
                                {
                                        status[cc[j].pt[k]][cc[j].pt[(k+1)%3]]=false;
                                        chs.push_back(make_pair(cc[j].pt[k],cc[j].pt[(k+1)%3]));
                                }
                                swap(cc[j],cc[cc.size()-1]);
                                cc.pop_back();
                                j--;
                        }
                }
                for (int j=0;j<chs.size();j++)
                {
                        if (!status[chs[j].first][chs[j].second] && status[chs[j].second][chs[j].first])continue;
                        chs[j]=chs[chs.size()-1];
                        j--;
                        chs.pop_back();
                }
                for (int j=0;j<chs.size();j++)
                {
                        cc.push_back(face(i,chs[j].first,chs[j].second));
                        status[i][chs[j].first]=status[chs[j].first][chs[j].second]=status[chs[j].second][i]=true;
                }
                for (int j=0;j<n;j++)
                        for (int k=0;k<n;k++)
                                assert(!(status[j][k]^status[k][j]));
        }
        //for (int j=0;j<cc.size();j++)cc[j].print();    printf("\n");
        real ans=0;
        for (int i=0;i<cc.size();i++)
                ans+=cc[i].ToSurface().nvect().len()/2;
        printf("%.6lf\n",abs(ans));
}
时间: 2024-10-11 10:42:36

bzoj 1209: [HNOI2004]最佳包裹 三维凸包的相关文章

bzoj 1964: hull 三维凸包 计算几何

1964: hull 三维凸包 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 54  Solved: 39[Submit][Status][Discuss] Description 三维凸包问题是一个基础的三维计算几何问题,不过这次你只需要做一个简单版的三维凸包问题就行了. Input 输入数据一共有若干行,每行三个整数,表示一个点的坐标.点的个数为五个. Output 输出一个实数,保留两位小数,表示三维凸包的体积. Sample Input 0

bzoj 1209

三维凸包裸题. 1.通过volume计算有向体积,判断点与面的位置关系. 2.噪声 1 /************************************************************** 2 Problem: 1209 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:20 ms 7 Memory:1864 kb 8 ********************************************

POJ3528 HDU3662 三维凸包模板

POJ3528 HDU3662 第一道题 给定若干点 求凸包的表面积,第二题 给定若干点就凸包的面数. 简单说一下三维凸包的求法,首先对于4个点假设不共面,确定了唯一四面体,对于一个新的点,若它不在四面体内,为了让它进入凸包, 则对于所有凸包上的边,若边的一面是该点可以看到的而另一面看不到,则该点与该边构成的面要加入凸包. 模板代码非常清晰, #include<stdio.h> #include<algorithm> #include<string.h> #includ

POJ 2225 / ZOJ 1438 / UVA 1438 Asteroids --三维凸包,求多面体重心

题意: 两个凸多面体,可以任意摆放,最多贴着,问他们重心的最短距离. 解法: 由于给出的是凸多面体,先构出两个三维凸包,再求其重心,求重心仿照求三角形重心的方式,然后再求两个多面体的重心到每个多面体的各个面的最短距离,然后最短距离相加即为答案,因为显然贴着最优. 求三角形重心见此: http://www.cnblogs.com/whatbeg/p/4234518.html 代码:(模板借鉴网上模板) #include <iostream> #include <cstdio> #in

三维凸包模板

poj3528 参照 #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; #define inf 0x7fffffff #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) #define eps 1e-7 #define MAXV 505 //三维点 s

Hnoi2004 金属包裹

我是萌萌的传送门 三维凸包模板题--只是听了听计算几何的课之后心血来潮想写的-- 我的做法很无脑是吧--暴力枚举三个点组成的三角形,然后枚举剩下的点,判断其余点是否都在这个三角形的同一侧,是的话则说明这个三角形是凸包的一个面. 理论复杂度应该是$O(n^4)$,不过看上去跑得飞快?人帅自带小常数哈哈 这个故事告诉我们:大力出奇迹-- 1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include&l

hdu4266(三维凸包模板题)

/*给出三维空间中的n个顶点,求解由这n个顶点构成的凸包表面的多边形个数. 增量法求解:首先任选4个点形成的一个四面体,然后每次新加一个点,分两种情况: 1> 在凸包内,则可以跳过 2> 在凸包外,找到从这个点可以"看见"的面,删除这些面, 然后对于一边没有面的线段,和新加的这个点新建一个面,至于这个点可以看见的面, 就是求出这个面的方程(可以直接求法向量). */ #include<iostream> #include<cmath> #includ

Hdu 3662 3D Convex Hull(三维凸包)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3662 思路:三维凸包模板. #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define PR 1e-8 #define N 510 using namespace std; struct TPoint { doub

hdu4449Building Design(三维凸包+平面旋转)

链接 看了几小时也没看懂代码表示的何意..无奈下来问问考研舍友. 还是考研舍友比较靠谱,分分钟解决了我的疑问. 可能三维的东西在纸面上真的不好表示,网上没有形象的题解,只有简单"明了"的讲解. 这题说起来很简单,求下三维凸包,枚举每一个面,进行坐标旋转,使得当前面作为xoy面时的其他坐标,然后求下投影面的凸包的面积. 为什么要旋转面而不直接算点到面的距离,是因为投影的面积没有办法算. 面旋转时是怎么旋转的,首先求得当前面的法向量p1,再求得它与向量e(0,0,1)的法向量pp,所有的点