1231 - Super Star
問題概要
空間上にn個の点がある。それらをすべて包含するような、最小の球の半径を求めよう。球面上に点があっても包含しているものとする。
ただし 4<=n<=30 とする。
コード
#include<iostream> #include<vector> #include<cmath> using namespace std; typedef double elem; const elem eps = 1.0e-8; const elem inf = 1.0e20; bool eq(elem a, elem b){return abs(b-a)<eps;} bool leq(elem a, elem b){return eq(a,b)||a<b;} struct point{ elem x,y,z; point(elem x, elem y, elem z):x(x),y(y),z(z){} point():x(0),y(0),z(0){} const point operator+(const point &t)const{ return point(x+t.x,y+t.y,z+t.z); } const point operator-(const point &t)const{ return point(x-t.x,y-t.y,z-t.z); } const point operator*(elem t)const{ return point(t*x,t*y,t*z); } const point operator/(elem t)const{ return point(x/t,y/t,z/t); } elem length()const{ return sqrt(x*x+y*y+z*z); } elem dot(const point &t)const{ return x*t.x+y*t.y+z*t.z; } }; ostream &operator<<(ostream &os, const point &t){ os << '(' << t.x << ',' << t.y << ',' << t.z << ')'; return os; } const point far_point(inf,inf,inf); typedef point vec; struct sphere{ point center; elem r; sphere ():center(0.0,0.0,0.0),r(0.0){} sphere (point c, elem r):center(c),r(r){} bool inside(const point &t)const{ return leq( (t-center).length(), r ); } }; inline point centerPoint(point a, point b) { return (a+b)/2; } inline point circumscribedCircleCenter(point a, point b, point c) { vec va=b-a; vec vb=c-a; elem A=va.length(); elem B=vb.length(); elem C=va.dot(vb)/(A*B); elem ds=2*A*pow(C,2.0)-2*A; elem dt=2*B*pow(C,2.0)-2*B; if( eq(ds,0.0) || eq(dt,0.0) ) return far_point; elem s=(B*C-A)/(2*A*pow(C,2.0)-2*A); elem t=(A*C-B)/(2*B*pow(C,2.0)-2*B); return a+va*s+vb*t; } inline point circumscribedTetrahedronCenter(point a, point b, point c, point d) { vec B=b-a;elem la=B.length(); vec C=c-a;elem lb=C.length(); vec D=d-a;elem lc=D.length(); elem delta = B.x*(C.y*D.z-D.y*C.z)+C.x*(D.y*B.z-B.y*D.z)+D.x*(B.y*C.z-C.y*B.z); if( eq( delta, 0.0 ) ) return far_point; elem x=(B.y*C.z-C.y*B.z)*pow(lc,2.0)+(D.y*B.z-B.y*D.z)*pow(lb,2.0)+(C.y*D.z-D.y*C.z)*pow(la,2.0); elem y=(C.x*B.z-B.x*C.z)*pow(lc,2.0)+(B.x*D.z-D.x*B.z)*pow(lb,2.0)+(D.x*C.z-C.x*D.z)*pow(la,2.0); elem z=(B.x*C.y-C.x*B.y)*pow(lc,2.0)+(D.x*B.y-B.x*D.y)*pow(lb,2.0)+(C.x*D.y-D.x*C.y)*pow(la,2.0); x/=2;y/=2;z/=2; return a+point(x,y,z)/delta; } elem solve(const vector<point> &vp) { elem answer = inf; // nC2 for(int i = 0; i < vp.size(); ++i){ for(int j = i + 1; j < vp.size(); ++j){ bool isContainingBall = true; sphere s(centerPoint(vp[i],vp[j]),(vp[i]-vp[j]).length()/2); for(int k = 0; k < vp.size(); ++k){ if( !s.inside( vp[k] ) ){ isContainingBall = false; break; } } if( isContainingBall ){ answer = leq( s.r, answer ) ? s.r : answer; } } } // nC3 for(int i = 0; i < vp.size(); ++i){ for(int j = i + 1; j < vp.size(); ++j){ for(int k = j + 1; k < vp.size(); ++k){ bool isContainingBall = true; point cent = circumscribedCircleCenter(vp[i],vp[j],vp[k]); sphere s(cent, (vp[i]-cent).length() ); for(int l = 0; l < vp.size(); ++l){ if( !s.inside( vp[l] ) ){ isContainingBall = false; break; } } if( isContainingBall ){ answer = leq( s.r, answer ) ? s.r : answer; } } } } // nC4 for(int i = 0; i < vp.size(); ++i){ for(int j = i + 1; j < vp.size(); ++j){ for(int k = j + 1; k < vp.size(); ++k){ for(int l = k + 1; l < vp.size(); ++l ){ bool isContainingBall = true; point cent = circumscribedTetrahedronCenter(vp[i],vp[j],vp[k],vp[l]); sphere s(cent, (vp[i]-cent).length() ); for(int m = 0; m < vp.size(); ++m){ if( !s.inside( vp[m] ) ){ isContainingBall = false; break; } } if( isContainingBall ){ answer = leq( s.r, answer ) ? s.r : answer; } } } } } return answer; } int main() { while(true){ int n; vector<point> vp; cin >> n; if( n == 0 ) break; for(int i = 0; i < n; ++i){ elem x,y,z; cin >> x >> y >> z; vp.push_back( point(x,y,z) ); } printf("%.7lf\n", solve(vp)); } return 0; }
解法
全探索する。
任意の異なる2点を直径とする球、3点の外心を大円とする球、4点が成す四面体の外接球を求める。
2点を直径とする球は自明。
3点の外心を大円とする球の中心の位置ベクトルpは、各頂点の位置ベクトルを0,a,bとすれば、
p=sa+tb, s = (BC-A)/(2AC2-2A), t = (AC-B)/(2BC2-2B)
A=nrm(a),B=nrm(b),C=a・b/AB, nrm(x) は x の大きさ
となる。
また、四面体の方は、上記同様、各頂点の位置ベクトルを0,a,b,cとすれば
正方行列 A=(a b c) として
nrm(p) = nrm(p-a) = nrm(p-b) = nrm(p-c)
より
tAp=0.5t(|a|2 |b|2 |c|2)
p=0.5 tA-1 t(|a|2 |b|2 |c|2)
より比較的簡単な以下の式が得られる。
http://twitpic.com/2ac056/full
また、4点のうちを1点を原点に置かず、代数的な方法で計算すると以下のような式になる。
http://twitpic.com/29xcbg/full
参考: http://homepage2.nifty.com/PAF00305/math/triangle/node7.html
その他の解法
- move to front ヒューリスティック
- 八分木
- 山登り法