sgu290:Defend the Milky Way(三维凸包)

题目大意:

给出空间中 n(1≤n≤100) 个点,求出其凸包。如果有点在凸包的面或棱上,也要将其算进凸包中,将答案按字典序输出。

分析:

首先我就不吐槽 sgu 坑爹的输入了...

主要就是求解三维凸包,至于凸包面或棱上的点可以在求出凸包后再加进去。凸包上每个面都是三角形,如果在平面中就类似三角剖分一般。

这里采用的是O(n2)的增量法,主要算法流程就是:

(1) 初始化一个未退化的四面体;

(2) 如果新点在凸包内或凸包上,忽视掉;

(3) 如果新点在凸包外,删去它可以看到的面,并将其并入整个凸包中。

对于 (1) 我们要对三点共线,四点共面的情况分别判断。

对于 (2) 主要就是我们维护凸包上每个面的法向量朝外,通过对点积判断正负来判断是否能看到该面。对于由 A,B,C 构成的法向量为 law?→? 的三角形,如果点 P 能看到该面当且仅当 AP?→??law?→?>0 ,如果一个面都看不到,那么说明该点在凸包内。

对于 (3) 就是把能看到的面删去,同时将点 P 与凸包未被删去的轮廓相连,也就是那些只被删去过一次的边。

有图有真相:

AC code:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <string>
#include <sstream>
#include <iostream>
#include <map>
#include <set>
#include <list>
#include <stack>
#include <queue>
#include <vector>
#define vec pot
#define sqr(x) ((x)*(x))
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define pb push_back
#define mp make_pair
#define clr(a, b) memset(a, b, sizeof a)
#define rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define per(i, a, b) for(int i = (a); i >= (b); --i)
#define sqr(x) ((x)*(x))
typedef long long LL;
typedef double DB;
typedef long double LD;
using namespace std;

void open_init()
{
    #ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    #endif
}

void close_file()
{
    #ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    #endif
}

const int MAXN = 109;

int n;
string input;
string name[MAXN];
string ansn[MAXN];
int tot;
struct pot
{
    LL x, y, z;
    pot(LL x = 0, LL y = 0, LL z = 0):x(x),y(y),z(z){}
    friend pot operator + (const pot &a, const pot &b)
    {
        return pot(a.x+b.x, a.y+b.y, a.z+b.z);
    }
    friend pot operator - (const pot &a, const pot &b)
    {
        return pot(a.x-b.x, a.y-b.y, a.z-b.z);
    }
    friend LL operator * (const pot &a, const pot &b)
    {
        return a.x*b.x+a.y*b.y+a.z*b.z;
    }
    friend pot operator ^ (const pot &a, const pot &b)
    {
        return pot(a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x);
    }
}v[MAXN];
struct tri
{
    int p[4];
    pot law;
};
tri s[MAXN*MAXN];
int stot;
set<int> valid, ans;
int bel[MAXN][MAXN];
bool del[MAXN*MAXN];
queue<int> q;

inline void read(int &pos, LL &x)
{
    int tmp = pos, i, f = 1;x = 0;
    while(input[pos] == ‘-‘ || isdigit(input[pos])) pos--;
    i = pos+1, pos--;
    if(input[i] == ‘-‘) f = -1;
    else x = input[i]-‘0‘;
    while(isdigit(input[++i])) x = x*10+input[i]-‘0‘;
    x *= f;
}

inline bool zero(const pot &a)
{
    return !a.x && !a.y && !a.z;
}

inline bool collinear(int a, int b, int c)
{
    return zero((v[a]-v[b])^(v[a]-v[c]));
}

inline bool coplanar(int a, int b, int c, int d)
{
    return ((v[b]-v[a])^(v[c]-v[a]))*(v[d]-v[a]) == 0;
}

inline void end()
{
    cout << n << endl;
    sort(name+1, name+n+1);
    rep(i, 1, n)
        cout << name[i] << endl;
    exit(0);
}

pot law(int a, int b, int c)
{
    return (v[b]-v[a])^(v[c]-v[a]);
}

inline void add(int a, int b, int c)
{
    tri node;
    node.p[3] = node.p[0] = a, node.p[1] = b, node.p[2] = c;
    node.law = law(a, b, c);s[++stot] = node;valid.insert(stot);
    rep(i, 0, 2) bel[node.p[i]][node.p[i+1]] = stot;
}

inline void init(int a, int b, int c, int d)
{
    if(law(a, b, c)*(v[d]-v[a]) < 0) add(a, b, c);
    else add(a, c, b);
}

inline bool in(int a, int b, int c, int d)
{
    return ((v[b]-v[a])^(v[d]-v[a]))*((v[d]-v[a])^(v[c]-v[a])) >= 0;
}

inline bool in_tri(const tri &a, int b)
{
    int x(a.p[0]), y(a.p[1]), z(a.p[2]);
    if(!coplanar(a.p[0], a.p[1], a.p[2], b)) return false;
    return in(x, y, z, b) && in(y, z, x, b) && in(z, x, y, b);
}

int main()
{
    open_init();

    scanf("%d\n", &n);
    rep(i, 1, n)
    {
        char c;
        getline(cin, input);
        int j = input.size()-1;
        while((c=input[j]) == ‘\n‘ || c == ‘\r‘ || c == ‘ ‘)
            j--;
        read(j, v[i].z), read(j, v[i].y), read(j, v[i].x);
        name[i] = input.substr(0, j+1);
    }
    if(n == 1) end();
    int ch1 = 0;
    rep(i, 3, n)
        if(!collinear(1, 2, i))
        {
            ch1 = i;
            break;
        }
    if(!ch1) end();
    int ch2 = 0;
    rep(i, 3, n)
        if(i != ch1 && !coplanar(1, 2, ch1, i))
        {
            ch2 = i;
            break;
        }
    if(!ch2) end();
    init(1, 2, ch1, ch2), init(1, 2, ch2, ch1);
    init(2, ch1, ch2, 1), init(1, ch1, ch2, 2);
    set<int>::iterator it;
    rep(i, 3, n)
        if(i != ch1 && i != ch2)
        {
            bool flag = false;
            for(it = valid.begin(); it != valid.end(); ++it)
                if(s[*it].law*(v[i]-v[s[*it].p[0]]) > 0)
                {
                    flag = true;
                    q.push(*it);
                    del[*it] = true;
                }
            if(!flag) continue;
            while(!q.empty())
            {
                int now = q.front();q.pop();
                valid.erase(now);
                rep(j, 0, 2)
                    if(!del[bel[s[now].p[j+1]][s[now].p[j]]])
                        add(i, s[now].p[j], s[now].p[j+1]);
            }
        }
    for(it = valid.begin(); it != valid.end(); ++it)
        rep(i, 0, 2)
            ans.insert(s[*it].p[i]);
    rep(i, 1, n)
        if(!ans.count(i))
            for(it = valid.begin(); it != valid.end(); ++it)
                if(in_tri(s[*it], i))
                    ans.insert(i);
    for(it = ans.begin(); it != ans.end(); ++it)
        ansn[++tot] = name[*it];
    cout << tot << endl;
    sort(ansn+1, ansn+tot+1);
    rep(i, 1, tot)
        cout << ansn[i] << endl;

    close_file();
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-02 17:05:38

sgu290:Defend the Milky Way(三维凸包)的相关文章

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

hdu4266(三维凸包模板题)

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

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

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,所有的点

hdu4273Rescue(三维凸包重心)

链接 模板题已不叫题.. 三维凸包+凸包重心+点到平面距离(体积/点积)  体积-->混合积(先点乘再叉乘) 1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<

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

1209: [HNOI2004]最佳包裹 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 160  Solved: 58[Submit][Status][Discuss] Description H 公司生产了一种金属制品,是由一些笔直的金属条支撑起来的,金属条和别的金属条在交点上被焊接在了一起.现在由于美观需要,在这个产品用一层特殊的材料包 裹起来.公司为了节约成本,希望消耗的材料最少(不计裁剪时的边角料的损失).你的程序需要根据给定的输入,给出