2015
09-16

# Throw the Stones

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 13    Accepted Submission(s): 2

Problem Description
Remember our childhood? A few naked children throw stones standing on the same position, the one throws farther win the game. Aha, of course, there are some naughty boys who care more about whether they can urinate father.
You believe it or not, anyway, I believed. Nowadays, some of the children are smarter than we were, while others may be more naughty.
A week ago, I saw several children throw stones. In fact, they are more clever than we were, since the game they played, apparently, is more complex than we did. Maybe you have different points of view, however, you’d better learn about the rules of the game
before expressing your views. A group of children take turns to throw stones standing on the same position. After some child throw a stone, the children will draw a convex polyhedron with smallest volume together to enclose all the stones thrown by them. You
may assume that the stone is so small as to be abstracted as a point in three-dimensional space. Naively, the children regard the space enclosed by the convex polyhedron as territory under their control. After a child throw his stone, the score he obtains
equals the incremental of the volume of their territory.   Unfortunately, the first three child’s score will always be zero. At last, the child with the highest score will win the game, and known as the "King".
I think you have accepted my opinion already, for the rules of their throwing stones game are really complicated. But, you also don’t need to be frustrated for it. Now, in order to show you are smarter, maybe you can write a program to help the children point
out their "King".

Input
Input consists of a number of cases. The data of each case appears on a number of input lines, the first of which contains an integer N. The following N lines contain three number (xi, yi, zi) indicating
coordinates of the stone thrown by the i-th child.
Note: 1 <= N <= 10^4, 1 <= i <= N, -10^4 <= xi , yi , zi <= 10^4.

Output
For each test case, you should output two lines. The first line is "Case #K:", K means the number of the test case. The second line is "i v", i means index of the "King" and v means the score of the "King". If there are more than
one "King", output the one throws stone earlier than others.
Please round the result to 2 digits after decimal point if necessary.

Sample Input
4
1 0 0
1 1 0
0 1 0
0 0 1
5
1 0 0
1 1 0
0 1 0
0 0 0
0 0 1

Sample Output
Case #1：
4 0.17
Case #2：
5 0.33

Source

Recommend
zhoujiaqi2010

题意较简单，略去。

典型的三维计算几何问题。

题目难度为偏难。

最简单直接的方法是暴力计算三维凸包

时间复杂度为O(n^2)，超时。

解题思路：

记录每个面的外法向量，类似邻接表的方式存储凸包记录相邻面信息。

对于新添加进来的一个石子（视作点光源），剔除当前凸包中的所有可见面，并更新维护凸包。

在删除和维护的过程中动态的更新凸包的体积，记录最大体积增量。

时间复杂度为O(n*sqrt(n))

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

#define db double
#define rt return
#define cs const
#define cp const P&
#define op operator

cs db eps = 1e-9;

inline int sig(db x){rt (x>eps)-(x<-eps);}

cs int N = 10001;

struct P {
db x, y, z;
P(db a = 0, db b = 0, db c = 0):x(a),y(b),z(c){}
void in(){scanf("%lf%lf%lf", &x, &y, &z);}
P op+(cp a)cs{rt P(x+a.x, y+a.y, z+a.z);}
P op-(cp a)cs{rt P(x-a.x, y-a.y, z-a.z);}
P op*(cs db&k)cs{rt P(x*k, y*k, z*k);}
P op^(cp a)cs{rt P(y*a.z-a.y*z, z*a.x-a.z*x, x*a.y-a.x*y);}
db op*(cp a)cs{rt x*a.x + y*a.y + z*a.z;}
P cross(P a, P b){rt a-*this ^ b-*this;}
db dot(P a, P b){rt (a-*this) * (b-*this);}
db L(){rt sqrt(x*x + y*y + z*z);}
db V6(P b, P c){rt (b ^ c) * (*this);}
}p[N];

int n;

db diff_vol, max_diff;

struct convex {
int cnt, blg[N][N];
struct face {
int a, b, c, is;
face(int x=0,int y=0,int z=0,int w=0):a(x),b(y),c(z),is(w){}
int visible(P me) {
rt sig(p[a].cross(p[b],p[c]) * (me-p[a])) > 0;
}
}fac[N*10];
int col(int a, int b, int c){rt p[a].cross(p[b],p[c]).L() < eps;}
int cop(int a, int b, int c, int d){rt !sig(p[a].cross(p[b],p[c])*(p[d]-p[a]));}
void deal(int k, int a, int b) {
int f = blg[a][b];
if(fac[f].is) {
if(fac[f].visible(p[k])) dfs(k, f);
else {
diff_vol += p[b].V6(p[a], p[k]);
face add = face(b, a, k, 1);
blg[b][a] = blg[a][k] = blg[k][b] = cnt;
}
}
}
void dfs(int k, int cur) {
diff_vol -= p[fac[cur].a].V6(p[fac[cur].b], p[fac[cur].c]);
fac[cur].is = 0;
deal(k, fac[cur].b, fac[cur].a);
deal(k, fac[cur].c, fac[cur].b);
deal(k, fac[cur].a, fac[cur].c);
}
void init() {
cnt = 0;
for(int i = 0; i < 4; i++) {
face add = face((i+1)%4, (i+2)%4, (i+3)%4, 1);
}
}
void update(int k) {
for(int i = 0; i < cnt; i++)
if(fac[i].is && fac[i].visible(p[k])) {
dfs(k, i); break;
}
}
db volume() {
db v = 0.;
for(int i = 0; i < cnt; i++)
v += fac[i].is * p[fac[i].a].V6(p[fac[i].b], p[fac[i].c]);
rt v / 6.;
}
}hull;

void solve(int number, int cas) {
max_diff = 0.;
int king = 1, tag = 1;
p[0].in();
for(int i = 1; i < number; i++) {
p[i].in();
if(tag == 1) {
tag += sig((p[0]-p[i]).L());
if(tag > 1) swap(p[1], p[i]);
continue;
}
if(tag == 2) {
tag += sig((p[0].cross(p[1], p[i])).L());
if(tag > 2) swap(p[2], p[i]);
continue;
}
if(tag == 3) {
tag += sig(p[0].cross(p[1], p[2]) * (p[i]-p[0])) != 0;
if(tag > 3) {
swap(p[3], p[i]);
hull.init();
for(int j = 4; j <= i; j++) hull.update(j);
king = i + 1, max_diff = hull.volume();
}
continue;
}
diff_vol = 0.;
hull.update(i);
diff_vol /= 6.;
if(sig(diff_vol - max_diff) > 0) {
max_diff = diff_vol;
king = i + 1;
}
}
printf("Case #%d:\n%d %.2lf\n", cas, king, max_diff);
}

int main() {
//freopen("Throw_The_Stones.in", "r", stdin);
//freopen("testThrow_The_Stones.out", "w", stdout);
int number, cas = 1;
while(scanf("%d", &number) != -1) solve(number, cas++);
rt 0;
}