AOJ 1171 Laser Beam Reflections(レーザー光の反射)

問題

座標平面上に、線分で表される鏡がn枚置いてある。
鏡は両面でレーザー光を反射することができる。

スタートからゴールまで、レーザー光をたどり着かせるための、
光の最短移動距離を求めよ。

制約条件

n≦5
座標は全て100以下の整数。
最適解において反射は5回以下であることが保証されている。
最適解において鏡の両端から0.001以下の点で光が反射することはない。

方針

使う鏡とその順番を全通り試す。


鏡で光が反射されることを、
鏡を対称軸に、全ての点が線対称に移動すると考える。
すると、光線はただのまっすぐな線分として考えることが出来る。


線分が、使う鏡の順番どおりに鏡と交わらなかったり、
途中で他の鏡にぶつかってしまったりしたらその解は捨てる。

ソースコード

int n;
vector<L> ms;
P start, goal;

double solve(vi &use){
	vector<L> tms = ms;
	P target = goal;
	rep(i, use.size()){
		L line = tms[use[i]];
		rep(j, n) rep(k, 2) tms[j][k] = reflection(line, tms[j][k]);
		target = reflection(line, target);
	}
	P cur = start;
	tms = ms;
	
	rep(i, use.size()){
		L beam(cur, target);
		if(!intersectSS(beam, tms[use[i]])) return INF; //使う鏡と交わらない
		
		P next = crosspoint(beam, tms[use[i]]);
		beam = L(cur, next);
		rep(j, n) if(j != use[i]){
			int tmp = ccw(cur, tms[j][0], tms[j][1]);
			if(tmp == 0 || tmp == 2 || !intersectSS(beam, tms[j])) continue;
			
			return INF; //使わない鏡と交わる
		}
		
		L line = tms[use[i]];
		rep(j, n) rep(k, 2) tms[j][k] = reflection(line, tms[j][k]);
		cur = next;
	}
	rep(i, n){
		int tmp = ccw(cur, tms[i][0], tms[i][1]);
		if(tmp == 0 || tmp == 2) continue;
		if(intersectSS(L(cur, target), tms[i])) return INF;
	}
	
	return abs(start - target);
}

double ans;
void rec(vi &use){
	ans = min(ans, solve(use));
	if(use.size() < 5){
		rep(i, n) if(use.empty() || use.back() != i){
			use.pb(i);
			rec(use);
			use.pop_back();
		}
	}
}

int main()
{
	while(cin >> n, n){
		ms.clear();
		
		rep(i, n){
			int x, y, X, Y;
			cin >> x >> y >> X >> Y;
			ms.pb(L(P(x, y), P(X, Y)));
		}
		cin >> goal.real() >> goal.imag();
		cin >> start.real() >> start.imag();
		
		ans = INF;
		vi use;
		rec(use);
		printf("%.20f\n", ans);
	}
	return 0;
}