AOJ0237 The Last Door
問題リンク The Last Door
- 解法
e[i][j]: 三角形iを光らせたときの光が三角形jに当たるか否か
という表を作ってから、最小何回で全体を光らせるかの処理に入ります。
e[i][j]を作るところから説明します。
3点の中で、三角形の頂点となる点を調べます。これは、2等辺三角形であることを利用して、(k, i)と(k, j)の距離が等しいような点kが頂点となります。底辺の頂点i, jの中点をmとし、mkベクトルを正規化します。正規化したmkベクトルをD倍し、底辺i, jに加えて、i+mk, j+mkとします。これら2つの点と底辺のi, jの点が、三角形を光らせたときにできる長方形の4頂点となります。
三角形iを光らせたときに三角形jに当たるかどうかは次のいずれかが成り立っているかを調べます。
1. jの内いずれかの点が長方形の中にある
2. jの内いずれかの点と、長方形との距離が0.01以下である
3. jの内のいずれかの辺と、長方形の4頂点のいずれかの点の距離が0.01以下である
4. 長方形の辺とjの三角形の辺のうち、交差・接触しているものがある
これで表e[i][j]が作れます。
三角形を頂点にし、e[i][j]を有向辺とするグラフが作れます。
まずはこれを強連結成分分解します。アルゴリズムを実装するのは簡単です。
これで出来上がった連結成分のグラフにおいて、入次数が0となっている所は、手をかざして光らせないとどうしようもない点です。それ以外は、光の連鎖で光らせることができます。すなわち、入次数が0になっている連結成分の数が光らせる最小回数となります。
- ソース
import java.util.Arrays; import java.util.Comparator; import java.util.PriorityQueue; import java.util.Scanner; //The Last Door public class AOJ0237 { int N, D; double EPS = 1e-8, T = 1e-2; double[][][] p; double[][] d; int[] top; int[][] bottom; boolean[][] e; boolean[][] rev; boolean[] visit; int ID; int[] id; int[] scc; void dfs(int v){ if(visit[v])return; visit[v] = true; for(int i=0;i<N;i++)if(e[v][i])dfs(i); id[v] = ID++; } void rdfs(int v){ if(visit[v])return; visit[v] = true; scc[v] = ID; for(int i=0;i<N;i++)if(rev[v][i])rdfs(i); } boolean hit(int a, int b){ double[] b1 = p[a][bottom[a][0]], b2 = p[a][bottom[a][1]]; double[] p1 = new double[]{b1[0]+d[a][0], b1[1]+d[a][1]}; double[] p2 = new double[]{b2[0]+d[a][0], b2[1]+d[a][1]}; double area = norm(b1, b2)*D*2; for(int j=0;j<3;j++){ double s = 0; s += area(b1, b2, p[b][j]) + area(b2, p2, p[b][j]) + area(p2, p1, p[b][j]) + area(p1, b1, p[b][j]); if(Math.abs(s-area)<1e-8)return true; } for(int j=0;j<3;j++){ if(dist(b1, b2, p[b][j])<T+EPS)return true; if(dist(b2, p2, p[b][j])<T+EPS)return true; if(dist(p2, p1, p[b][j])<T+EPS)return true; if(dist(p1, b1, p[b][j])<T+EPS)return true; } for(int j=0;j<3;j++){ if(dist(p[b][j], p[b][(j+1)%3], b1)<T+EPS)return true; if(dist(p[b][j], p[b][(j+1)%3], b2)<T+EPS)return true; if(dist(p[b][j], p[b][(j+1)%3], p1)<T+EPS)return true; if(dist(p[b][j], p[b][(j+1)%3], p2)<T+EPS)return true; } for(int j=0;j<3;j++){ if(crossing(b1, b2, p[b][j], p[b][(j+1)%3]))return true; if(crossing(b2, p2, p[b][j], p[b][(j+1)%3]))return true; if(crossing(p2, p1, p[b][j], p[b][(j+1)%3]))return true; if(crossing(p1, b1, p[b][j], p[b][(j+1)%3]))return true; } return false; } double ex(double[] a, double[] b, double[] c){ double[] s1 = sub(b, a), s2 = sub(c, a); return cross(s1, s2); } boolean crossing(double[] a, double[] b, double[] s, double[] t){ if(Math.abs(cross(sub(b, a), sub(t, s)))<EPS){ return Math.min(dist(a, b, s), Math.min(dist(a, b, t), Math.min(dist(s, t, a), dist(s, t, b))))<EPS; } if(ex(a, b, s)*ex(a, b, t)>0)return false; if(ex(b, a, s)*ex(b, a, t)>0)return false; if(ex(s, t, a)*ex(s, t, b)>0)return false; return ex(t, s, a)*ex(t, s, b)<0; } double dist(double[] a, double[] b, double[] p){ if(dot(sub(b, a), sub(p, a))<EPS)return norm(a, p); if(dot(sub(a, b), sub(p, b))<EPS)return norm(b, p); return Math.abs(cross(sub(b, a), sub(p, a)))/norm(a, b); } double dot(double[] a, double[] b){ return a[0]*b[0]+a[1]*b[1]; } double cross(double[] a, double[] b){ return a[0]*b[1]-a[1]*b[0]; } double norm(double[] a, double[] b){ return Math.hypot(a[0]-b[0], a[1]-b[1]); } double[] sub(double[] a, double[] b){ return new double[]{a[0]-b[0], a[1]-b[1]}; } double[] mid(double[] a, double[] b){ return new double[]{(a[0]+b[0])/2, (a[1]+b[1])/2}; } double area(double[] a, double[] b, double[] c){ double res = cross(a, b)+cross(b, c)+cross(c, a); return Math.abs(res); } void run(){ Scanner sc = new Scanner(System.in); for(;;){ N = sc.nextInt(); D = sc.nextInt(); if((N|D)==0)break; p = new double[N][3][2]; d = new double[N][2]; top = new int[N]; bottom = new int[N][2]; for(int i=0;i<N;i++){ for(int j=0;j<3;j++)for(int k=0;k<2;k++)p[i][j][k] = sc.nextDouble(); for(int j=0;j<3;j++){ if(Math.abs(norm(p[i][j], p[i][(j+1)%3])-norm(p[i][j], p[i][(j+2)%3]))<EPS){ top[i] = j; bottom[i][0] = (j+1)%3; bottom[i][1] = (j+2)%3; double[] m = mid(p[i][(j+1)%3], p[i][(j+2)%3]); d[i] = sub(p[i][j], m); double nor = Math.hypot(d[i][0], d[i][1]); d[i][0]/=nor; d[i][1]/=nor; d[i][0]*=D; d[i][1]*=D; break; } } } e = new boolean[N][N]; rev = new boolean[N][N]; for(int i=0;i<N;i++)for(int j=0;j<N;j++){ if(i==j)continue; e[i][j] = hit(i, j); rev[j][i] = e[i][j]; } visit = new boolean[N]; ID = 0; id = new int[N]; for(int i=0;i<N;i++){ if(visit[i])continue; dfs(i); } PriorityQueue<Integer> q = new PriorityQueue<Integer>(N, new Comparator<Integer>() { public int compare(Integer o1, Integer o2) { return id[o2]-id[o1]; } }); for(int i=0;i<N;i++)q.add(i); scc = new int[N]; ID = 0; Arrays.fill(visit, false); while(!q.isEmpty()){ int v = q.poll(); if(visit[v])continue; rdfs(v); ID++; } int[] deg = new int[ID]; for(int i=0;i<N;i++)for(int j=0;j<N;j++){ if(i==j||scc[i]==scc[j]||!e[i][j])continue; deg[scc[j]]++; } int res = 0; for(int i=0;i<ID;i++)if(deg[i]==0)res++; System.out.println(res); } } public static void main(String[] args) { new AOJ0237().run(); } }