Problem:
Problem E: Kuru-Kuru Robot

ロボット工学の研究者であるアシモフ博士は、新しいロボットの開発に成功した。このロボットは、床に張り巡らせた特殊なワイヤーに沿って自由に動くことができる。ロボットは常に現在いるワイヤーに平行な方向を向いて前進する。

すばらしいことに、このロボットはワイヤー上を移動するためにその距離にかかわらずエネルギーを消費しない。しかし、ワイヤーの端点やワイヤーの交点で向きを変えるためにはその点を中心に回転する必要があり、その回転角度だけエネルギーを消費する。

このロボットをスタート地点からゴール地点まで動かしたい。例えば、下図においてロボットを start に東向き（下図 x軸の正の向き）で配置し goal まで動かしてみよう。start から交点 A 交点 B を経由して goal にたどり着く経路と、交点 A 交点 C を経由する経路があるが、前者の方が移動距離が長いものの回転角度が少ないためエネルギー消費量は少ない。

あなたの仕事は、床にあるワイヤーの位置、スタート地点、ゴール地点を読み込み、スタート地点からゴール地点へ移動するために必要な回転角度の合計の最小値を報告するプログラムを作成することである。

ロボットはスタート地点においてどのような向きに配置してもよく、ゴール地点ではどの方向を向いていてもよい。

Input

入力は複数のデータセットからなる。各データセットの形式は以下のとおり：

n
x11 y11 x21 y21
x12 y12 x22 y22
.
.
.
x1n y1n x2n y2n
sx sy gx gy

n はワイヤーの数を示す整数である。x1i y1i は i 番目のワイヤーの一方の端点の座標、x2i y2i は i 番目のワイヤーのもう一方の端点の座標を示す。

sx sy、gx gy はそれぞれスタート地点とゴール地点の座標を示す。

与えられる座標はすべて整数値であり、x軸 y軸の値は -1000　以上 1000 以下である。また、スタート地点とゴール地点は与えられるワイヤーの端点上にあると仮定してよい。

n ≤ 50 であると仮定してよい。

n が 0 のとき入力の終了を示す。

Output

各データセットに対して、回転角度の合計の最小値を１行に出力せよ。ロボットがゴール地点にたどり着けない場合は "-1" と出力せよ。出力は 0.00001 以下の誤差を含んでもよい。

Sample Input

8
1 3 11 3
5 3 13 11
10 10 17 10
11 2 11 6
10 5 14 5
13 6 13 2
17 10 17 3
13 3 19 3
1 3 19 3
6
1 3 11 3
5 3 13 11
10 10 17 10
11 2 11 6
10 5 14 5
13 3 19 3
1 3 19 3
2
0 0 7 0
3 0 7 3
0 0 7 3
0

Output for the Sample Input

270.0000
-1
36.86989765

Solution in JAVA (judge verdict: Wrong Answer)


import java.util.*;
import java.math.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;
import static java.math.BigInteger.*;

public class Main {
	public static void main(String[] args) {
		new Main().run();
	}

	void debug(Object... os) {
//		System.err.println(deepToString(os));
	}

	void run() {
		Scanner sc = new Scanner(System.in);
		for (;;) {
			int n = sc.nextInt();
			if (n == 0)
				return;
			S[] ss = new S[n];
			for (int i = 0; i < n; i++) {
				ss[i] = new S(new P(sc.nextDouble(), sc.nextDouble()), new P(sc.nextDouble(), sc.nextDouble()));
			}
			P st = new P(sc.nextDouble(), sc.nextDouble());
			P go = new P(sc.nextDouble(), sc.nextDouble());
			if (st.equals(go)) {
				System.out.println(0);
				continue;
			}
			A[][] segs = new A[n][2];
			for (int i = 0; i < n; i++) {
				ArrayList<P> list = new ArrayList<P>();
				list.add(ss[i].p1);
				list.add(ss[i].p2);
				if (ss[i].contains(st) == 1)
					list.add(st);
				if (ss[i].contains(go) == 1)
					list.add(go);
				for (int j = 0; j < n; j++)
					if (i != j) {
						if (ss[i].contains(ss[j].p1) == 1 && !list.contains(ss[j].p1))
							list.add(ss[j].p1);
						if (ss[i].contains(ss[j].p2) == 1 && !list.contains(ss[j].p2))
							list.add(ss[j].p2);

						P cr = ss[i].intersection(ss[j]);
						if (cr != null && !list.contains(cr))
							list.add(cr);
					}
				sort(list);
				segs[i][0] = new A();
				segs[i][1] = new A();
				for (int j = 1; j < list.size(); j++) {
					segs[i][0].add(new S(list.get(j - 1), list.get(j)));
					segs[i][1].add(new S(list.get(j), list.get(j - 1)));
				}
//				debug()
			}
			V[][][] vs = new V[n][2][];
			PriorityQueue<E> que = new PriorityQueue<E>();
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < 2; j++) {
					vs[i][j] = new V[segs[i][0].size()];
					for (int k = 0; k < vs[i][j].length; k++) {
						vs[i][j][k] = new V();
						if (segs[i][j].get(k).p2.equals(go))
							vs[i][j][k].goal = true;
						if (segs[i][j].get(k).p1.equals(st)) {
							vs[i][j][k].d = 0;
							que.offer(new E(vs[i][j][k], 0));
						}
					}
					debug(segs[i][j]);
				}
			}
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < 2; j++) {
					for (int k = 0; k < vs[i][j].length; k++) {
						for (int i2 = 0; i2 < n; i2++) {
							for (int j2 = 0; j2 < 2; j2++) {
								for (int k2 = 0; k2 < vs[i2][j2].length; k2++) {
									S s = segs[i][j].get(k), t = segs[i2][j2].get(k2);
									if (s.p2.equals(t.p1)) {
										vs[i][j][k].es.add(new E(vs[i2][j2][k2], s.arg(t) / PI * 180));
									}
								}
							}
						}
					}
				}
			}
			double res = INF;
			while (!que.isEmpty()) {
				V now = que.poll().to;
//				debug(now);
				if (now.goal) {
					res = min(res, now.d);
//					break;
				}
				for (E e : now.es) {
//					debug(e);
					if (e.to.d > now.d + e.d) {
						e.to.d = now.d + e.d;
						que.offer(new E(e.to, now.d + e.d));
					}
				}
			}
			System.out.println(res == INF ? "-1" : res);
		}
	}

	class A extends ArrayList<S> {
	}

	int INF = 1 << 28;

	class V {
		boolean goal = false;
		ArrayList<E> es = new ArrayList<E>();
		double d = INF;

		@Override
		public String toString() {
			return "" + d;
		}
	}

	class E implements Comparable<E> {
		V to;
		final double d;

		E(V to, double d) {
			this.to = to;
			this.d = d;
		}

		@Override
		public String toString() {
			// 
			return "" + d;
		}

		@Override
		public int compareTo(E o) {
			return (int) signum(d - o.d);
		}
	}

	boolean eq(double a, double b) {
		return abs(a - b) < EPS;
	}

	class S {
		P p1, p2;

		double arg(S s) {
			return p2.sub(p1).arg(s.p2.sub(s.p1));
		}

		public S(P p1, P p2) {
			this.p1 = p1;
			this.p2 = p2;
		}

		P intersection(S s) {// IF 'truly' intersect '1' point THEN point ELSE null.
			P r = p2.sub(p1), t = s.p2.sub(s.p1);
			double d = t.det(r);
			if (sig(d) == 0)
				return null;// parallel.
			P p = p1.add(r.mul(t.det(s.p1.sub(p1)) / d)); // intersection of line.
			if (contains(p) >= 0 && s.contains(p) >= 0)
				return p;
			return null;
		}

		int contains(P p) { // OUT,ON,IN -> -1,0,1
			if (p1.equals(p) || p2.equals(p))
				return 0;
			P r1 = p1.sub(p), r2 = p2.sub(p);
			if (eq(r1.det(r2), 0) && eq(r1.dot(r2), -r1.norm() * r2.norm()))
				return 1;
			return -1;
		}

		@Override
		public String toString() {
			return "S [p1=" + p1 + ", p2=" + p2 + "]";
		}

	}

	class P implements Comparable<P> {
		ArrayList<P> ps = new ArrayList<P>();
		double x, y;

		P add(P p) {
			return new P(x + p.x, y + p.y);
		}

		P sub(P p) {
			return new P(x - p.x, y - p.y);
		}

		P mul(double d) {
			return new P(x * d, y * d);
		}

		P div(double d) {
			return new P(x / d, y / d);
		}

		double dot(P p) {
			return x * p.x + y * p.y;
		}

		double det(P p) {
			return x * p.y - y * p.x;
		}

		double norm() {
			return hypot(x, y);
		}

		double dist(P p) {
			return sub(p).norm();
		}

		double arg(P p) {
			return acos(dot(p) / (norm() * p.norm()));
		}

		@Override
		public boolean equals(Object obj) {
			P other = (P) obj;
			return compareTo(other) == 0;
		}

		public P(double x, double y) {
			super();
			this.x = x;
			this.y = y;
		}

		@Override
		public int compareTo(P o) {
			return sig(x - o.x) != 0 ? sig(x - o.x) : sig(y - o.y);
		}

		@Override
		public String toString() {
			return "[" + x + ", " + y + "]";
		}
	}

	int sig(double d) {
		return d < -EPS ? -1 : d > EPS ? 1 : 0;
	}

	double EPS = 1e-7;
}