AOJ1190 Anchored Balloon
問題
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1190&lang=jp
Z=0の平面上にN個の整数座標が与えられる。各々に杭を打ち、それぞれ異なる長さのロープで一つのバルーンに繋ぐ。
上空に飛ばしたとき、バルーンの上昇する最大の高さを求めよ。
1 <= N <= 10
−100 <= X[i] <= 100, −100 <= Y[i] <= 100, 1 <= L[i] <= 300
解答
それぞれのロープがL[i]の長さを持つ。1つのロープの自由度を想像してみると、半径L[i]の半球をなすことが分かる。
よってN個の半球が作れる。バルーンが飛ぶことのできる位置は、N個全ての半球の交わる領域に限られる。
その領域のうち、高さが最大となる位置はどこかを調べたい。
最大となる高さを二分探索でZ = Hと決め打ちしよう。すると、全ての半球はZ = Hの平面で切断されるか、平面に接触するかとなる。すなわち、Z = Hの平面上には半径0以上のN個の半球の切断面となる円が描かれる。各々の円の半径は、ロープの長さと切断する平面の高さZ = Hにより三平方の定理を用いれば求まる。中心座標は高さが変化しただけなので、杭の位置と変わらない。
Z = H平面上で切断面の円が交差するならば、3次元平面上でも領域が交差するので、問題を3次元から2次元平面上の問題に帰着できた。あとは、全ての円が交わる領域が存在するか調べればよい。その境界となる高さが答えとなる。
全ての円が交わるためには、まず任意の2円が、交わっている or 一方が他方を包含している必要がある。言い換えると、2円が外側に離れているケースがあれば不適となる。
しかし、これは交差領域の存在判定の必要条件であるが、十分条件は満たさない。実際にサンプルの一部が合うが、サンプル出力より少し高い位置を示す出力が得られる。
3円が互いに交わっていても、間に隙間ができる場合がある。この場合はすべての円が交わらない領域が生じてしまう。
よって、任意の2円の交点のうち少なくともどちらか一方を、異なる任意の円が包含している必要がある。
このとき2円が(互いに外側に離れていることはないが)包含関係にあることによって交点を生成しない場合があるので、そのような場合を省いて交わる場合のみ考えることに注意する。
任意の3円が交わる領域が存在すれば、4円以上を考慮する必要はないので、これで十分となる。
int N; double x[11], y[11], l[11]; bool compare(double h) { vector<circle> cs; rep(i, N) { if(l[i] < h - EPS) return false; cs.push_back({{x[i], y[i]}, sqrt(sq(l[i]) - sq(h))}); } rep(i, N) REP(j, i+1, N) { if(out(cs[i], cs[j])) return false; } rep(i, N) REP(j, i+1, N) REP(k, j+1, N) { auto p = crosspoint(cs[i], cs[j]); auto q = crosspoint(cs[j], cs[k]); auto r = crosspoint(cs[k], cs[i]); if(!contains(cs[k], p.first) && !contains(cs[k], p.second) && !contains(cs[i], q.first) && !contains(cs[i], q.second) && !contains(cs[j], r.first) && !contains(cs[j], r.second) && !contains(cs[i], cs[j]) && !contains(cs[j], cs[k]) && !contains(cs[k], cs[i])) return false; } return true; } double solve() { double l = 0.0, r = 1e10; rep(_, 150) { double m = (l + r) / 2.0; (compare(m) ? l : r) = m; } return l; } int main() { for(; cin >> N && N;) { rep(i, N) { cin >> x[i] >> y[i] >> l[i]; } printf("%.10f\n", solve()); } return 0; }