問題
3次元空間 N 点の凸包の表面積と体積を求めよ。
制約
$4 \le N \le 2000$
$-10^4 \le x_i, y_i, z_i \le 10^4$(整数)
誤差 $10^{-6}$ 以下
入出力例
入力例 1
8
0 0 0
1 0 0
0 1 0
1 1 0
0 0 1
1 0 1
0 1 1
1 1 1
出力例 1
6.000000
1.000000
ヒント (段階的開示)
ヒント1: 方向性
3D 凸包は Incremental Algorithm または QuickHull 3D で構築。体積は四面体分割。
ヒント2: アプローチ
$V = \frac{1}{6} \sum |A \cdot (B \times C)|$。面の法線を外向きに統一。
ヒント3: 誘導
点から見える面を削除 → ホライゾンエッジから新面を張る。
模範解答 (Python)
import sys
from math import sqrt
EPS = 1e-9
def cross3(a, b):
return (a[1]*b[2] - a[2]*b[1],
a[2]*b[0] - a[0]*b[2],
a[0]*b[1] - a[1]*b[0])
def dot3(a, b):
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
def sub3(a, b):
return (a[0]-b[0], a[1]-b[1], a[2]-b[2])
def norm3(a):
return sqrt(a[0]**2 + a[1]**2 + a[2]**2)
def signed_vol(a, b, c):
return dot3(a, cross3(b, c))
def visible(face_pts, p, pts):
a, b, c = pts[face_pts[0]], pts[face_pts[1]], pts[face_pts[2]]
n = cross3(sub3(b, a), sub3(c, a))
return dot3(n, sub3(p, a)) > EPS
def convex_hull_3d(pts):
n = len(pts)
faces = []
init = list(range(4))
a, b, c, d = [pts[i] for i in init]
if dot3(cross3(sub3(b,a), sub3(c,a)), sub3(d,a)) > 0:
init[0], init[1] = init[1], init[0]
a, b, c, d = init
faces = [[a, b, c], [a, c, d], [a, d, b], [b, d, c]]
for i in range(4, n):
p = pts[i]
visible_faces = [f for f in faces if visible(f, p, pts)]
if not visible_faces:
continue
horizon = []
edge_count = {}
for f in visible_faces:
for e in [(f[0],f[1]), (f[1],f[2]), (f[2],f[0])]:
key = tuple(sorted(e))
edge_count[key] = edge_count.get(key, 0) + 1
for f in visible_faces:
for e in [(f[0],f[1]), (f[1],f[2]), (f[2],f[0])]:
key = tuple(sorted(e))
if edge_count[key] == 1:
horizon.append(e)
faces = [f for f in faces if not visible(f, p, pts)]
for e in horizon:
new_face = [e[0], e[1], i]
faces.append(new_face)
return faces
def solve():
n = int(input())
pts = []
for _ in range(n):
x, y, z = map(int, input().split())
pts.append((x, y, z))
faces = convex_hull_3d(pts)
surface_area = 0.0
volume_6x = 0.0
for f in faces:
a, b, c = pts[f[0]], pts[f[1]], pts[f[2]]
ab = sub3(b, a); ac = sub3(c, a)
n_vec = cross3(ab, ac)
surface_area += norm3(n_vec) / 2.0
volume_6x += signed_vol(a, b, c)
volume = abs(volume_6x) / 6.0
print(f"{surface_area:.6f}")
print(f"{volume:.6f}")
solve()
Step-by-Step 解説
1Incremental 構築
点を1つずつ追加し、見える面を削除、ホライゾンから新面。
点を1つずつ追加し、見える面を削除、ホライゾンから新面。
2体積計算
ガウスの定理で原点-面の四面体の符号付き体積の和。
ガウスの定理で原点-面の四面体の符号付き体積の和。
3数値精度
整数入力なら cross/dot は正確。最終のみ float に変換。
整数入力なら cross/dot は正確。最終のみ float に変換。
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| 面の向き不統一 | ホライゾンの向きミス | 新面の法線を外向きに確認 |
| EPS 設定 | 退化ケース判定失敗 | 整数入力なら EPS=0 |
| 初期四面体の退化 | 最初の4点が同一平面 | 一般位置の4点を探索 |
次のステップ
- Minkowski 和(3Dグラフ + 凸幾何)