ACM-ICPC 2010国内予選 G. レーザー光の反射
解法
- レーザーが当たる鏡の順番を全列挙する
- 反射回数は高々5で,鏡の枚数も高々5なのでそんなに多くない
- 各反射順ごとに,レーザーの照射角度を求める
- 反射順に鏡を選び,照射地点の鏡像を取っていく
- 最終的に得られる照射地点の像の方向が、照射角度の候補になる
- 照射角度の候補それぞれについて、実際に照射距離を計算する
- 光の進行を素直にシミュレーションすれば良い
- 一番近い反射点を求め,次の照射角を計算していく
- 候補の計算がいい加減なので、ちゃんとゴールに着くかも調べる必要がある
- 調べたもののうち,最も照射距離が小さかったものを返す
#include <iostream> #include <vector> #include <complex> using namespace std; typedef complex<double> P; struct L { P p, q; L(P p, P q) : p(p), q(q) {} }; const double EPS = 1e-8; const double INF = 1e12; double dot(P a, P b) { return real(conj(a)*b); } double cross(P a, P b) { return imag(conj(a)*b); } double angle(P a, P b) { return arg(conj(a)*b); } P footPerp(L l, P p) { return l.p+(l.q-l.p)*(dot(l.q-l.p,p-l.p) / (abs(l.q-l.p)*abs(l.q-l.p))); } P lineShadow(L l, P p){ return 2.0*footPerp(l, p)-p; } bool ssIntersect(L a, L b){ if(abs(imag((a.q-a.p)/(b.q-b.p)))<EPS) return false; return cross(a.q-a.p, b.p-a.p)*cross(a.q-a.p, b.q-a.p) < 0 && cross(b.q-b.p, a.p-b.p)*cross(b.q-b.p, a.q-b.p) < 0; } bool lpIntersect(L a, P p){ return abs(imag((p-a.p)/(a.q-a.p)))<EPS; } P ssCrosspoint(L a, L b){ double A = cross(a.q-a.p, b.q-b.p); double B = cross(a.q-a.p, a.q-b.p); return b.p + B/A * (b.q-b.p); } void check(vector<double> &agl, const vector<L> &vl, const vector<int> &idx, P s, P g){ for(int i=0;i<idx.size();i++){ if(lpIntersect(vl[idx[i]], g)) return ; g = lineShadow(vl[idx[i]], g); } agl.push_back(angle(P(1,0), g-s)); } void dfs(vector<double> &agl, const vector<L> &vl, vector<int> &idx, P s, P g){ check(agl, vl, idx, s, g); if(idx.size()==5) return ; for(int i=0;i<vl.size();i++){ if(!idx.empty()&&idx.back()==i) continue; idx.push_back(i); dfs(agl, vl, idx, s, g); idx.pop_back(); } } double calc(const vector<double> &agl, const vector<L> &vl, P s, P g){ double res = INF; for(int i=0;i<agl.size();i++){ P cur = s; P dir = P(cos(agl[i]), sin(agl[i])); double len = 0.0; for(int cnt=0; ;cnt++){ P nxt = cur + 1000.0*dir; if(lpIntersect(L(cur, cur+dir), g)&&dot(dir, g-cur)>EPS) nxt = g; int idx = -1; for(int j=0;j<vl.size();j++){ if(!lpIntersect(vl[j], cur)&&ssIntersect(L(cur, nxt), vl[j])){ idx = j; nxt = ssCrosspoint(L(cur, nxt), vl[j]); } } len += abs(cur-nxt); if(abs(nxt-g)<EPS) break; if(idx == -1 || cnt == 6){ len = INF; break; } dir = lineShadow(L(nxt, nxt+(vl[idx].q-vl[idx].p)*P(0,1)), cur) - nxt; dir /= abs(dir); cur = nxt; } res = min(res, len); } return res; } int main(){ int n; while(cin >> n, n){ P s, g; vector<L> vl; for(int i=0;i<n;i++){ int px, py, qx, qy; cin >> px >> py >> qx >> qy; vl.push_back(L(P(px,py), P(qx,qy))); } int tx, ty, lx, ly; cin >> tx >> ty >> lx >> ly; s = P(tx, ty); g = P(lx, ly); vector<int> idx; vector<double> vd; dfs(vd, vl, idx, s, g); printf("%.4lf\n", calc(vd, vl, s,g)); } }